diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index db7a2654c..4c1c18700 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -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 diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2016MayToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2016MayToR5Loader.java index 8ea11a987..39f5c87ba 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2016MayToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2016MayToR5Loader.java @@ -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()); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2ToR5Loader.java index 2326dfe3a..f15d06347 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R2ToR5Loader.java @@ -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()); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R3ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R3ToR5Loader.java index 9997f8e75..c4b0296af 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R3ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R3ToR5Loader.java @@ -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()); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4BToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4BToR5Loader.java index bc8241154..762d55df5 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4BToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4BToR5Loader.java @@ -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()); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java index f0aeea721..c076997a5 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java @@ -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()); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R5ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R5ToR5Loader.java index c008b30fe..b33bfbea5 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R5ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R5ToR5Loader.java @@ -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()); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R6ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R6ToR5Loader.java index 4cc02e101..7ee4540db 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R6ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R6ToR5Loader.java @@ -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()); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactory.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactory.java index 8b56fbad7..6b9e06eb9 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactory.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactory.java @@ -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; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java index c931badc1..7fed97d56 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java @@ -221,4 +221,9 @@ public class TerminologyClientR2 implements ITerminologyClient { client.setContentLanguage(lang); return this; } + + @Override + public int getUseCount() { + return client.getUseCount(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java index 0048022eb..0f2cff0cc 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java @@ -223,4 +223,10 @@ public class TerminologyClientR3 implements ITerminologyClient { client.setContentLanguage(lang); return this; } + + @Override + public int getUseCount() { + return client.getUseCount(); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java index fb0a0600c..aaf1e571d 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java @@ -235,4 +235,11 @@ public class TerminologyClientR4 implements ITerminologyClient { client.setContentLanguage(lang); return this; } + + @Override + public int getUseCount() { + return client.getUseCount(); + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactoryTest.java b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactoryTest.java index 6fc50be4e..a00eea1aa 100644 --- a/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactoryTest.java +++ b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/txClient/TerminologyClientFactoryTest.java @@ -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"); } ); } diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/client/FHIRToolingClient.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/client/FHIRToolingClient.java index e30978120..00d02580a 100644 --- a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/client/FHIRToolingClient.java @@ -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 read(Class resourceClass, String id) {// TODO Change this to AddressableResource + recordUse(); ResourceRequest result = null; try { result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -231,6 +233,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T vread(Class resourceClass, String id, String version) { + recordUse(); ResourceRequest 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 getCanonical(Class resourceClass, String canonicalURL) { + recordUse(); ResourceRequest result = null; try { result = utils.issueGetResourceRequest( @@ -279,6 +283,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public Resource update(Resource resource) { + recordUse(); ResourceRequest result = null; try { List
headers = null; @@ -315,6 +320,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T update(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest result = null; try { List
headers = null; @@ -515,6 +521,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { // } public Parameters operateType(Class 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 OperationOutcome validate(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest 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
headers = null; ResourceRequest result = utils.issuePostRequest( resourceAddress.resolveOperationUri(ValueSet.class, "expand"), @@ -734,6 +745,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public Parameters lookupCode(Map params) { + recordUse(); ResourceRequest 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 params) { + recordUse(); List
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
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++; + } } \ No newline at end of file diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java index 938d501be..08f2f3ee6 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java @@ -79,6 +79,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { private EnumSet 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 read(Class resourceClass, String id) {//TODO Change this to AddressableResource + recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -204,6 +206,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T vread(Class resourceClass, String id, String version) { + recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), @@ -221,6 +224,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T getCanonical(Class resourceClass, String canonicalURL) { + recordUse(); ResourceRequest 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 result = null; try { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()), @@ -270,6 +275,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T update(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest result = null; try { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -297,6 +303,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public Parameters operateType(Class 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 OperationOutcome validate(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest 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 result = null; @@ -426,6 +437,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { public Parameters lookupCode(Map params) { + recordUse(); org.hl7.fhir.dstu3.utils.client.network.ResourceRequest 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 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 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 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++; + } } diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java index 0527c7a22..3dab5b6e3 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java @@ -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 result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -178,6 +180,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T read(Class resourceClass, String id) {// TODO Change this to AddressableResource + recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -194,6 +197,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T vread(Class resourceClass, String id, String version) { + recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest( @@ -211,6 +215,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T getCanonical(Class resourceClass, String canonicalURL) { + recordUse(); ResourceRequest 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 result = null; try { result = client.issuePutRequest( @@ -263,6 +269,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T update(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest result = null; try { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -292,6 +299,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public Parameters operateType(Class 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 OperationOutcome validate(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest 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 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 result = null; @@ -440,6 +453,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public Parameters lookupCode(Map params) { + recordUse(); org.hl7.fhir.r4.utils.client.network.ResourceRequest 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 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 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 fetchResource(Class resourceClass, String id) { + recordUse(); org.hl7.fhir.r4.utils.client.network.ResourceRequest 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++; + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index bf0b2b690..490333870 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -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> allResourcesById = new HashMap>(); @@ -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 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 params = new HashMap(); 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 systems = findRelevantSystems(vs); + TerminologyClientContext tc = tcc.chooseServer(systems, true); + addDependentResources(tc, p, vs); Map params = new HashMap(); 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 systems = new HashSet<>(); + Set 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 systems) { + private Bundle processBatch(TerminologyClientContext tc, Bundle batch, Set 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 systems = new HashSet<>(); + Set 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 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 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 findRelevantSystems(ValueSet vs) { + Set set = new HashSet<>(); + if (vs != null) { + findRelevantSystems(set, vs); + } + return set; + } + + private Set findRelevantSystems(CodeableConcept code, ValueSet vs) { + Set set = new HashSet<>(); + if (vs != null) { + findRelevantSystems(set, vs); + } + for (Coding c : code.getCoding()) { + findRelevantSystems(set, c); + } + return set; + } + + private Set findRelevantSystems(Coding code, ValueSet vs) { + Set set = new HashSet<>(); + if (vs != null) { + findRelevantSystems(set, vs); + } + if (code != null) { + findRelevantSystems(set, code); + } + return set; + } + + private void findRelevantSystems(Set 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 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 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); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IContextResourceLoader.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IContextResourceLoader.java index e2abf8b41..187aadc4e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IContextResourceLoader.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IContextResourceLoader.java @@ -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 diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index 177503ae9..b554b6f3f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -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; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/ITerminologyClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/ITerminologyClient.java index 45e3ce4ab..7eae719aa 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/ITerminologyClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/ITerminologyClient.java @@ -68,4 +68,5 @@ public interface ITerminologyClient { ITerminologyClient setAcceptLanguage(String lang); ITerminologyClient setContentLanguage(String lang); String getUserAgent(); + int getUseCount(); } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientContext.java index 14154562c..cc8393502 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientContext.java @@ -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 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 useCounts = new HashMap<>(); private TerminologyCapabilities txcaps; + private final Set 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 getCached() { - return cached; + protected TerminologyClientContext(ITerminologyClient client, boolean master) { + super(); + this.client = client; + this.master = master; } - public String getServer() { - return server; + public Map 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 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(); + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java new file mode 100644 index 000000000..98602f72e --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java @@ -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 serverList = new ArrayList<>(); // clients by server address + private Map serverMap = new HashMap<>(); // clients by server address + private Map resMap = new HashMap<>(); // client resolution list + private List 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 systems, boolean expand) throws TerminologyServiceException { + if (serverList.isEmpty()) { + return null; + } + if (systems.contains(UNRESOLVED_VALUESET)) { + return serverList.get(0); + } + + Set 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 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 serverMap() { + Map 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 getInternalErrors() { + return internalErrors; + } + + public List getServerList() { + return serverList; + } + + public Map getServerMap() { + return serverMap; + } + + public String getMonitorServiceURL() { + return monitorServiceURL; + } + + + +} diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientR5.java similarity index 86% rename from org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java rename to org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientR5.java index 42ea8d348..1d8d4a19b 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientR5.java @@ -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(); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java index 109ce8fb6..5859df699 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestPackageLoader.java @@ -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(); + } + } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java index 8d8067ad5..3ab463ba6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java @@ -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 result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -223,6 +227,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { public T read(Class resourceClass, String id) {//TODO Change this to AddressableResource + recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -240,6 +245,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T vread(Class resourceClass, String id, String version) { + recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), @@ -257,6 +263,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T getCanonical(Class resourceClass, String canonicalURL) { + recordUse(); ResourceRequest 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 result = null; try { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()), @@ -306,6 +314,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public T update(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest result = null; try { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), @@ -333,6 +342,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { } public Parameters operateType(Class 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 OperationOutcome validate(Class resourceClass, T resource, String id) { + recordUse(); ResourceRequest 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 result = null; @@ -457,6 +471,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient { public Parameters lookupCode(Map params) { + recordUse(); org.hl7.fhir.r5.utils.client.network.ResourceRequest 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 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 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 fetchResource(Class resourceClass, String id) { + recordUse(); org.hl7.fhir.r5.utils.client.network.ResourceRequest 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; + } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java index 2b14634a1..2fdd73e3e 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java @@ -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")); } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/SimpleWorkerContextTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/SimpleWorkerContextTests.java index 94bbebbe5..b3586d28c 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/SimpleWorkerContextTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/SimpleWorkerContextTests.java @@ -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); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index f8f154987..ee063279f 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -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; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/parser/JsonParser.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/parser/JsonParser.java index 01089ffc3..fd0893e57 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/parser/JsonParser.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/parser/JsonParser.java @@ -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(); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 206533f26..05b81a143 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -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() + ")"; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java index c98945a8d..c8d1fed40 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java @@ -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) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java index 16035fa28..5d279ad9a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java @@ -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"); } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java index 1d962e6b6..1b40dd3b6 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationEngineTests.java @@ -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"); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index b6b6a8312..4c9b4272c 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -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")) {