001package org.hl7.fhir.r4.hapi.ctx; 002 003import ca.uhn.fhir.context.FhirContext; 004import ca.uhn.fhir.context.support.IContextValidationSupport; 005import ca.uhn.fhir.rest.api.Constants; 006import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 007import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 008import ca.uhn.fhir.util.CoverageIgnore; 009import com.github.benmanes.caffeine.cache.Cache; 010import com.github.benmanes.caffeine.cache.Caffeine; 011import org.apache.commons.lang3.Validate; 012import org.apache.commons.lang3.time.DateUtils; 013import org.fhir.ucum.UcumService; 014import org.hl7.fhir.exceptions.FHIRException; 015import org.hl7.fhir.exceptions.TerminologyServiceException; 016import org.hl7.fhir.r4.context.IWorkerContext; 017import org.hl7.fhir.r4.formats.IParser; 018import org.hl7.fhir.r4.formats.ParserType; 019import org.hl7.fhir.r4.model.*; 020import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; 021import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; 022import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; 023import org.hl7.fhir.r4.terminologies.ValueSetExpander; 024import org.hl7.fhir.r4.terminologies.ValueSetExpanderFactory; 025import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple; 026import org.hl7.fhir.r4.utils.IResourceValidator; 027import org.hl7.fhir.utilities.TerminologyServiceOptions; 028import org.hl7.fhir.utilities.TranslationServices; 029import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 030 031import java.util.*; 032import java.util.concurrent.TimeUnit; 033 034import static org.apache.commons.lang3.StringUtils.isNotBlank; 035 036public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander, ValueSetExpanderFactory { 037 private final FhirContext myCtx; 038 private final Cache<String, Resource> myFetchedResourceCache; 039 private IValidationSupport myValidationSupport; 040 private Parameters myExpansionProfile; 041 private String myOverrideVersionNs; 042 043 public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) { 044 Validate.notNull(theCtx, "theCtx must not be null"); 045 Validate.notNull(theValidationSupport, "theValidationSupport must not be null"); 046 myCtx = theCtx; 047 myValidationSupport = theValidationSupport; 048 049 long timeoutMillis = 10 * DateUtils.MILLIS_PER_SECOND; 050 if (System.getProperties().containsKey(ca.uhn.fhir.rest.api.Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)) { 051 timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)); 052 } 053 054 myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build(); 055 } 056 057 @Override 058 public List<StructureDefinition> allStructures() { 059 return myValidationSupport.fetchAllStructureDefinitions(myCtx); 060 } 061 062 @Override 063 public List<StructureDefinition> getStructures() { 064 return allStructures(); 065 } 066 067 @Override 068 public CodeSystem fetchCodeSystem(String theSystem) { 069 if (myValidationSupport == null) { 070 return null; 071 } else { 072 return myValidationSupport.fetchCodeSystem(myCtx, theSystem); 073 } 074 } 075 076 @Override 077 public List<ConceptMap> findMapsForSource(String theUrl) { 078 throw new UnsupportedOperationException(); 079 } 080 081 @Override 082 public String getAbbreviation(String theName) { 083 throw new UnsupportedOperationException(); 084 } 085 086 @Override 087 public ValueSetExpander getExpander() { 088 ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this); 089 retVal.setMaxExpansionSize(Integer.MAX_VALUE); 090 return retVal; 091 } 092 093 @Override 094 public org.hl7.fhir.r4.utils.INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) { 095 throw new UnsupportedOperationException(); 096 } 097 098 @Override 099 public IParser getParser(ParserType theType) { 100 throw new UnsupportedOperationException(); 101 } 102 103 @Override 104 public IParser getParser(String theType) { 105 throw new UnsupportedOperationException(); 106 } 107 108 @Override 109 public List<String> getResourceNames() { 110 List<String> result = new ArrayList<>(); 111 for (ResourceType next : ResourceType.values()) { 112 result.add(next.name()); 113 } 114 Collections.sort(result); 115 return result; 116 } 117 118 @Override 119 public IParser newJsonParser() { 120 throw new UnsupportedOperationException(); 121 } 122 123 @Override 124 public IResourceValidator newValidator() { 125 throw new UnsupportedOperationException(); 126 } 127 128 @Override 129 public IParser newXmlParser() { 130 throw new UnsupportedOperationException(); 131 } 132 133 @Override 134 public String oid2Uri(String theCode) { 135 throw new UnsupportedOperationException(); 136 } 137 138 @Override 139 public boolean supportsSystem(String theSystem) { 140 if (myValidationSupport == null) { 141 return false; 142 } else { 143 return myValidationSupport.isCodeSystemSupported(myCtx, theSystem); 144 } 145 } 146 147 @Override 148 public Set<String> typeTails() { 149 return new HashSet<>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code", 150 "Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint", 151 "Timing", "Reference", "Annotation", "Signature", "Meta")); 152 } 153 154 @Override 155 public ValidationResult validateCode(TerminologyServiceOptions theOptions, CodeableConcept theCode, ValueSet theVs) { 156 for (Coding next : theCode.getCoding()) { 157 ValidationResult retVal = validateCode(theOptions, next, theVs); 158 if (retVal.isOk()) { 159 return retVal; 160 } 161 } 162 163 return new ValidationResult(IssueSeverity.ERROR, null); 164 } 165 166 @Override 167 public ValidationResult validateCode(TerminologyServiceOptions theOptions, Coding theCode, ValueSet theVs) { 168 String system = theCode.getSystem(); 169 String code = theCode.getCode(); 170 String display = theCode.getDisplay(); 171 return validateCode(theOptions, system, code, display, theVs); 172 } 173 174 @Override 175 public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay) { 176 IContextValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, (String)null); 177 if (result == null) { 178 return null; 179 } 180 return new ValidationResult((IssueSeverity)result.getSeverity(), result.getMessage(), (ConceptDefinitionComponent)result.asConceptDefinition()); 181 } 182 183 @Override 184 public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) { 185 throw new UnsupportedOperationException(); 186 } 187 188 189 @Override 190 public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay, ValueSet theVs) { 191 192 /* 193 * The following valueset is a special case, since the BCP codesystem is very difficult to expand 194 */ 195 if ("http://hl7.org/fhir/ValueSet/languages".equals(theVs.getUrl())) { 196 ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); 197 definition.setCode(theSystem); 198 definition.setDisplay(theCode); 199 return new ValidationResult(definition); 200 } 201 202 /* 203 * The following valueset is a special case, since the mime types codesystem is very difficult to expand 204 */ 205 if ("http://hl7.org/fhir/ValueSet/mimetypes".equals(theVs.getUrl())) { 206 ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); 207 definition.setCode(theSystem); 208 definition.setDisplay(theCode); 209 return new ValidationResult(definition); 210 } 211 212 IValidationSupport.CodeValidationResult outcome; 213 if (isNotBlank(theVs.getUrl())) { 214 outcome = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay, theVs.getUrl()); 215 } else { 216 outcome = myValidationSupport.validateCodeInValueSet(myCtx, theSystem, theCode, theDisplay, theVs); 217 } 218 219 if (outcome != null && outcome.isOk()) { 220 ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); 221 definition.setCode(theCode); 222 definition.setDisplay(outcome.getDisplay()); 223 return new ValidationResult(definition); 224 } 225 226 return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + Constants.codeSystemWithDefaultDescription(theSystem) + "]"); 227 } 228 229 @Override 230 public ValidationResult validateCode(TerminologyServiceOptions theOptions, String code, ValueSet vs) { 231 return validateCode(theOptions, Constants.CODESYSTEM_VALIDATE_NOT_NEEDED, code, null, vs); 232 } 233 234 @Override 235 @CoverageIgnore 236 public List<MetadataResource> allConformanceResources() { 237 throw new UnsupportedOperationException(); 238 } 239 240 @Override 241 public void generateSnapshot(StructureDefinition p) throws FHIRException { 242 throw new UnsupportedOperationException(); 243 } 244 245 @Override 246 public Parameters getExpansionParameters() { 247 return myExpansionProfile; 248 } 249 250 @Override 251 public void setExpansionProfile(Parameters theExpParameters) { 252 myExpansionProfile = theExpParameters; 253 } 254 255 @Override 256 @CoverageIgnore 257 public boolean hasCache() { 258 throw new UnsupportedOperationException(); 259 } 260 261 @Override 262 public ValueSetExpansionOutcome expand(ValueSet theSource, Parameters theProfile) { 263 ValueSetExpansionOutcome vso; 264 try { 265 vso = getExpander().expand(theSource, theProfile); 266 } catch (InvalidRequestException e) { 267 throw e; 268 } catch (Exception e) { 269 throw new InternalErrorException(e); 270 } 271 if (vso.getError() != null) { 272 throw new InvalidRequestException(vso.getError()); 273 } else { 274 return vso; 275 } 276 } 277 278 @Override 279 public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHeiarchical) { 280 throw new UnsupportedOperationException(); 281 } 282 283 @Override 284 public ValueSetExpansionOutcome expandVS(ConceptSetComponent theInc, boolean theHeiarchical) throws TerminologyServiceException { 285 return myValidationSupport.expandValueSet(myCtx, theInc); 286 } 287 288 @Override 289 public ILoggingService getLogger() { 290 throw new UnsupportedOperationException(); 291 } 292 293 @Override 294 public void setLogger(ILoggingService theLogger) { 295 throw new UnsupportedOperationException(); 296 } 297 298 @Override 299 public String getVersion() { 300 return myCtx.getVersion().getVersion().getFhirVersionString(); 301 } 302 303 @Override 304 public UcumService getUcumService() { 305 throw new UnsupportedOperationException(); 306 } 307 308 @Override 309 public void setUcumService(UcumService ucumService) { 310 throw new UnsupportedOperationException(); 311 } 312 313 @Override 314 public boolean isNoTerminologyServer() { 315 return false; 316 } 317 318 @Override 319 public TranslationServices translator() { 320 throw new UnsupportedOperationException(); 321 } 322 323 @Override 324 public List<StructureMap> listTransforms() { 325 throw new UnsupportedOperationException(); 326 } 327 328 @Override 329 public StructureMap getTransform(String url) { 330 throw new UnsupportedOperationException(); 331 } 332 333 @Override 334 public String getOverrideVersionNs() { 335 return myOverrideVersionNs; 336 } 337 338 @Override 339 public void setOverrideVersionNs(String value) { 340 myOverrideVersionNs = value; 341 } 342 343 @Override 344 public StructureDefinition fetchTypeDefinition(String typeName) { 345 return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); 346 } 347 348 @Override 349 public String getLinkForUrl(String corePath, String url) { 350 throw new UnsupportedOperationException(); 351 } 352 353 @Override 354 public List<String> getTypeNames() { 355 throw new UnsupportedOperationException(); 356 } 357 358 @Override 359 public <T extends org.hl7.fhir.r4.model.Resource> T fetchResource(Class<T> theClass, String theUri) { 360 if (myValidationSupport == null) { 361 return null; 362 } else { 363 @SuppressWarnings("unchecked") 364 T retVal = (T) myFetchedResourceCache.get(theUri, t -> { 365 return myValidationSupport.fetchResource(myCtx, theClass, theUri); 366 }); 367 return retVal; 368 } 369 } 370 371 @Override 372 public <T extends org.hl7.fhir.r4.model.Resource> T fetchResourceWithException(Class<T> theClass, String theUri) throws FHIRException { 373 T retVal = fetchResource(theClass, theUri); 374 if (retVal == null) { 375 throw new FHIRException("Could not find resource: " + theUri); 376 } 377 return retVal; 378 } 379 380 @Override 381 public org.hl7.fhir.r4.model.Resource fetchResourceById(String theType, String theUri) { 382 throw new UnsupportedOperationException(); 383 } 384 385 @Override 386 public <T extends org.hl7.fhir.r4.model.Resource> boolean hasResource(Class<T> theClass_, String theUri) { 387 throw new UnsupportedOperationException(); 388 } 389 390 @Override 391 public void cacheResource(org.hl7.fhir.r4.model.Resource theRes) throws FHIRException { 392 throw new UnsupportedOperationException(); 393 } 394 395 @Override 396 public Set<String> getResourceNamesAsSet() { 397 return myCtx.getResourceNames(); 398 } 399 400 @Override 401 public ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHeiarchical) throws FHIRException { 402 throw new UnsupportedOperationException(); 403 } 404 405}