
001package org.hl7.fhir.r5.terminologies.client; 002 003import java.io.IOException; 004import java.net.MalformedURLException; 005import java.net.URL; 006import java.util.HashMap; 007import java.util.HashSet; 008import java.util.Map; 009import java.util.Set; 010 011import org.hl7.fhir.r5.model.CanonicalResource; 012import org.hl7.fhir.r5.model.CapabilityStatement; 013import org.hl7.fhir.r5.model.Extension; 014import org.hl7.fhir.r5.model.TerminologyCapabilities; 015import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent; 016import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesExpansionParameterComponent; 017import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.TerminologyClientContextUseCount; 018import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache; 019import org.hl7.fhir.r5.utils.ToolingExtensions; 020import org.hl7.fhir.utilities.ENoDump; 021import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 022import org.hl7.fhir.utilities.VersionUtilities; 023 024@MarkedToMoveToAdjunctPackage 025public class TerminologyClientContext { 026 027 public enum TerminologyClientContextUseType { 028 expand, validate, readVS, readCS 029 } 030 public class TerminologyClientContextUseCount { 031 private int expands; 032 private int validates; 033 private int readVS; 034 private int readCS; 035 036 public int getReadVS() { 037 return readVS; 038 } 039 public void setReadVS(int readVS) { 040 this.readVS = readVS; 041 } 042 public int getReadCS() { 043 return readCS; 044 } 045 public void setReadCS(int readCS) { 046 this.readCS = readCS; 047 } 048 public int getExpands() { 049 return expands; 050 } 051 public void setExpands(int expands) { 052 this.expands = expands; 053 } 054 public int getValidates() { 055 return validates; 056 } 057 public void setValidates(int validates) { 058 this.validates = validates; 059 } 060 } 061 062 private static final String MIN_TEST_VERSION = "1.6.0"; 063 private static boolean allowNonConformantServers = false; 064 private static boolean canAllowNonConformantServers = false; 065 066 private static boolean canUseCacheId; 067 068 private ITerminologyClient client; 069 private boolean initialised = false; 070 private CapabilityStatement capabilitiesStatement; 071 private TerminologyCapabilities txcaps; 072 private TerminologyCache txCache; 073 074 private Map<String, TerminologyClientContextUseCount> useCounts = new HashMap<>(); 075 private boolean isTxCaching; 076 private final Set<String> cached = new HashSet<>(); 077 private boolean master; 078 private String cacheId; 079 080 protected TerminologyClientContext(ITerminologyClient client, String cacheId, boolean master) { 081 super(); 082 this.client = client; 083 this.cacheId = cacheId; 084 this.master = master; 085 } 086 087 public Map<String, TerminologyClientContextUseCount> getUseCounts() { 088 return useCounts; 089 } 090 091 public boolean isMaster() { 092 return master; 093 } 094 095 public ITerminologyClient getClient() { 096 return client; 097 } 098 099 public void seeUse(Set<String> systems, TerminologyClientContextUseType useType) { 100 if (systems != null) { 101 for (String s : systems) { 102 seeUse(s, useType); 103 } 104 } 105 } 106 107 public void seeUse(String s, TerminologyClientContextUseType useType) { 108 TerminologyClientContextUseCount uc = useCounts.get(s); 109 if (uc == null) { 110 uc = new TerminologyClientContextUseCount(); 111 useCounts.put(s,uc); 112 } 113 switch (useType) { 114 case expand: 115 uc.expands++; 116 break; 117 case readVS: 118 uc.readVS++; 119 break; 120 case readCS: 121 uc.readCS++; 122 break; 123 case validate: 124 uc.validates++; 125 break; 126 default: 127 break; 128 } 129 } 130 131 public TerminologyCapabilities getTxCapabilities() { 132 return txcaps; 133 } 134 135 public void setTxCapabilities(TerminologyCapabilities txcaps) { 136 this.txcaps = txcaps; 137 } 138 139 public Set<String> getCached() { 140 return cached; 141 } 142 143 public boolean alreadyCached(CanonicalResource cr) { 144 return cached.contains(cr.getVUrl()); 145 } 146 147 public void addToCache(CanonicalResource cr) { 148 cached.add(cr.getVUrl()); 149 } 150 151 public String getAddress() { 152 return client.getAddress(); 153 } 154 155 public int getUseCount() { 156 return getClient().getUseCount(); 157 } 158 159 public boolean isTxCaching() { 160 return isTxCaching; 161 } 162 163 public void setTxCaching(boolean isTxCaching) { 164 this.isTxCaching = isTxCaching; 165 } 166 167 public boolean usingCache() { 168 return isTxCaching && cacheId != null; 169 } 170 171 public String getCacheId() { 172 return cacheId; 173 } 174 175 public TerminologyCache getTxCache() { 176 return txCache; 177 } 178 179 public void setTxCache(TerminologyCache txCache) { 180 this.txCache = txCache; 181 } 182 183 public void initialize() throws IOException { 184 if (!initialised) { 185 // we don't cache the quick CS - we want to know that the server is with us. 186 capabilitiesStatement = client.getCapabilitiesStatement(); 187 checkFeature(); 188 if (txCache != null && txCache.hasTerminologyCapabilities(getAddress())) { 189 txcaps = txCache.getTerminologyCapabilities(getAddress()); 190 if (txcaps.getSoftware().hasVersion() && !txcaps.getSoftware().getVersion().equals(capabilitiesStatement.getSoftware().getVersion())) { 191 txcaps = null; 192 } 193 } 194 if (txcaps == null) { 195 txcaps = client.getTerminologyCapabilities(); 196 if (txCache != null) { 197 txCache.cacheTerminologyCapabilities(getAddress(), txcaps); 198 } 199 } 200 if (txcaps != null && TerminologyClientContext.canUseCacheId) { 201 for (TerminologyCapabilitiesExpansionParameterComponent t : txcaps.getExpansion().getParameter()) { 202 if ("cache-id".equals(t.getName())) { 203 setTxCaching(true); 204 break; 205 } 206 } 207 } 208 initialised = true; 209 } 210 } 211 212 private void checkFeature() { 213 if (!allowNonConformantServers && capabilitiesStatement != null) { 214 String testVersion = null; 215 boolean csParams = false; 216 217 for (Extension t : capabilitiesStatement.getExtension()) { 218 if (ToolingExtensions.EXT_FEATURE.equals(t.getUrl())) { 219 String defn = t.getExtensionString("definition"); 220 if (ToolingExtensions.FEATURE_TX_TEST_VERSION.equals(defn)) { 221 testVersion = t.getExtensionString("value"); 222 } else if (ToolingExtensions.FEATURE_TX_CS_PARAMS.equals(defn)) { 223 csParams = "true".equals(t.getExtensionString("value")); 224 } 225 } 226 } 227 228 if (testVersion == null) { 229 if (canAllowNonConformantServers) { 230 throw new ENoDump("The terminology server "+client.getAddress()+" is not approved for use with this software (it does not pass the required tests).\r\nIf you wish to use this server, add the parameter -authorise-non-conformant-tx-servers to the command line parameters"); 231 } else { 232 throw new ENoDump("The terminology server "+client.getAddress()+" is not approved for use with this software (it does not pass the required tests)"); 233 } 234 } else if (!VersionUtilities.isThisOrLater(MIN_TEST_VERSION, testVersion)) { 235 if (canAllowNonConformantServers) { 236 throw new ENoDump("The terminology server "+client.getAddress()+" is not approved for use with this software as it is too old (test version = "+testVersion+").\r\nIf you wish to use this server, add the parameter -authorise-non-conformant-tx-servers to the command line parameters"); 237 } else { 238 throw new ENoDump("The terminology server "+client.getAddress()+" is not approved for use with this software as it is too old (test version = "+testVersion+")"); 239 } 240 } else if (!csParams) { 241 if (canAllowNonConformantServers) { 242 throw new ENoDump("The terminology server "+client.getAddress()+" is not approved for use as it does not accept code systems in the tx-resource parameter.\r\nIf you wish to use this server, add the parameter -authorise-non-conformant-tx-servers to the command line parameters"); 243 } else { 244 throw new ENoDump("The terminology server "+client.getAddress()+" is not approved for use as it does not accept code systems in the tx-resource parameter"); 245 } 246 } else { 247 // all good 248 } 249 } 250 } 251 252 public boolean supportsSystem(String system) throws IOException { 253 initialize(); 254 for (TerminologyCapabilitiesCodeSystemComponent tccs : txcaps.getCodeSystem()) { 255 if (system.equals(tccs.getUri())) { 256 return true; 257 } 258 } 259 return false; 260 } 261 262 @Override 263 public String toString() { 264 return client.getAddress(); 265 } 266 267 public static boolean isCanUseCacheId() { 268 return canUseCacheId; 269 } 270 271 public static void setCanUseCacheId(boolean canUseCacheId) { 272 TerminologyClientContext.canUseCacheId = canUseCacheId; 273 } 274 275 public String getHost() { 276 try { 277 URL uri = new URL(getAddress()); 278 return uri.getHost(); 279 } catch (MalformedURLException e) { 280 return getAddress(); 281 } 282 } 283 284 public static boolean isAllowNonConformantServers() { 285 return allowNonConformantServers; 286 } 287 288 public static void setAllowNonConformantServers(boolean allowNonConformantServers) { 289 TerminologyClientContext.allowNonConformantServers = allowNonConformantServers; 290 } 291 292 public static boolean isCanAllowNonConformantServers() { 293 return canAllowNonConformantServers; 294 } 295 296 public static void setCanAllowNonConformantServers(boolean canAllowNonConformantServers) { 297 TerminologyClientContext.canAllowNonConformantServers = canAllowNonConformantServers; 298 } 299 300}