
001package org.hl7.fhir.r4.utils.client; 002 003import java.io.IOException; 004import java.net.URI; 005import java.net.URISyntaxException; 006import java.util.*; 007 008import lombok.Getter; 009import lombok.Setter; 010import org.hl7.fhir.exceptions.FHIRException; 011import org.hl7.fhir.r4.model.Bundle; 012import org.hl7.fhir.r4.model.CapabilityStatement; 013import org.hl7.fhir.r4.model.CodeSystem; 014import org.hl7.fhir.r4.model.Coding; 015import org.hl7.fhir.r4.model.ConceptMap; 016import org.hl7.fhir.r4.model.OperationOutcome; 017import org.hl7.fhir.r4.model.Parameters; 018import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; 019import org.hl7.fhir.r4.model.PrimitiveType; 020import org.hl7.fhir.r4.model.Resource; 021import org.hl7.fhir.r4.model.StringType; 022import org.hl7.fhir.r4.model.TerminologyCapabilities; 023import org.hl7.fhir.r4.model.ValueSet; 024import org.hl7.fhir.r4.utils.client.network.ByteUtils; 025import org.hl7.fhir.r4.utils.client.network.Client; 026import org.hl7.fhir.r4.utils.client.network.ResourceRequest; 027import org.hl7.fhir.utilities.FHIRBaseToolingClient; 028import org.hl7.fhir.utilities.ToolingClientLogger; 029import org.hl7.fhir.utilities.Utilities; 030 031import org.hl7.fhir.utilities.http.HTTPHeader; 032 033/** 034 * Very Simple RESTful client. This is purely for use in the standalone tools 035 * jar packages. It doesn't support many features, only what the tools need. 036 * <p> 037 * To use, initialize class and set base service URI as follows: 038 * 039 * <pre> 040 * <code> 041 * FHIRSimpleClient fhirClient = new FHIRSimpleClient(); 042 * fhirClient.initialize("http://my.fhir.domain/myServiceRoot"); 043 * </code> 044 * </pre> 045 * <p> 046 * Default Accept and Content-Type headers are application/fhir+xml and 047 * application/fhir+json. 048 * <p> 049 * These can be changed by invoking the following setter functions: 050 * 051 * <pre> 052 * <code> 053 * setPreferredResourceFormat() 054 * setPreferredFeedFormat() 055 * </code> 056 * </pre> 057 * <p> 058 * TODO Review all sad paths. 059 * 060 * @author Claude Nanjo 061 */ 062public class FHIRToolingClient extends FHIRBaseToolingClient { 063 064 public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssK"; 065 public static final String DATE_FORMAT = "yyyy-MM-dd"; 066 public static final String hostKey = "http.proxyHost"; 067 public static final String portKey = "http.proxyPort"; 068 069 private String base; 070 private ResourceAddress resourceAddress; 071 @Setter 072 private ResourceFormat preferredResourceFormat; 073 private int maxResultSetSize = -1;// _count 074 @Getter 075 @Setter 076 private Client client = new Client(); 077 private List<HTTPHeader> headers = new ArrayList<>(); 078 079 @Getter @Setter 080 private String userAgent; 081 @Setter 082 private String acceptLanguage; 083 084 @Setter 085 private String contentLanguage; 086 private int useCount; 087 088 // Pass endpoint for client - URI 089 public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException { 090 preferredResourceFormat = ResourceFormat.RESOURCE_JSON; 091 this.userAgent = userAgent; 092 initialize(baseServiceUrl); 093 } 094 095 public void initialize(String baseServiceUrl) throws URISyntaxException { 096 base = baseServiceUrl; 097 client.setBase(base); 098 resourceAddress = new ResourceAddress(baseServiceUrl); 099 this.maxResultSetSize = -1; 100 } 101 102 public String getPreferredResourceFormat() { 103 return preferredResourceFormat.getHeader(); 104 } 105 106 public int getMaximumRecordCount() { 107 return maxResultSetSize; 108 } 109 110 public void setMaximumRecordCount(int maxResultSetSize) { 111 this.maxResultSetSize = maxResultSetSize; 112 } 113 114 public TerminologyCapabilities getTerminologyCapabilities() { 115 TerminologyCapabilities capabilities = null; 116 try { 117 capabilities = (TerminologyCapabilities) client.issueGetResourceRequest(resourceAddress.resolveMetadataTxCaps(), 118 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "TerminologyCapabilities", timeoutNormal).getReference(); 119 } catch (Exception e) { 120 throw new FHIRException("Error fetching the server's terminology capabilities", e); 121 } 122 return capabilities; 123 } 124 125 public CapabilityStatement getCapabilitiesStatementQuick() { 126 CapabilityStatement conformance = null; 127 try { 128 conformance = (CapabilityStatement) client.issueGetResourceRequest(resourceAddress.resolveMetadataUri(true), 129 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "CapabilitiesStatement", timeoutNormal).getReference(); 130 } catch (Exception e) { 131 throw new FHIRException("Error fetching the server's conformance statement", e); 132 } 133 return conformance; 134 } 135 136 public CapabilityStatement getCapabilitiesStatement() throws EFhirClientException { 137 try { 138 return (CapabilityStatement) client.issueGetResourceRequest(resourceAddress.resolveMetadataUri(false), 139 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "CapabilitiesStatement-Quick", timeoutNormal) 140 .getReference(); 141 } catch (Exception e) { 142 throw new FHIRException("Error fetching the server's capability statement: " + e.getMessage(), e); 143 } 144 } 145 146 public Resource read(String resourceClass, String id) {// TODO Change this to AddressableResource 147 recordUse(); 148 ResourceRequest<Resource> result = null; 149 try { 150 result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), 151 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "Read " + resourceClass + "/" + id, 152 timeoutNormal); 153 if (result.isUnsuccessfulRequest()) { 154 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 155 (OperationOutcome) result.getPayload()); 156 } 157 } catch (Exception e) { 158 throw new FHIRException(e); 159 } 160 return result.getPayload(); 161 } 162 163 public <T extends Resource> T read(Class<T> resourceClass, String id) {// TODO Change this to AddressableResource 164 recordUse(); 165 ResourceRequest<T> result = null; 166 try { 167 result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), 168 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "Read " + resourceClass.getName() + "/" + id, 169 timeoutNormal); 170 if (result.isUnsuccessfulRequest()) { 171 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 172 (OperationOutcome) result.getPayload()); 173 } 174 } catch (Exception e) { 175 throw new FHIRException(e); 176 } 177 return result.getPayload(); 178 } 179 180 public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) { 181 recordUse(); 182 ResourceRequest<T> result = null; 183 try { 184 result = client.issueGetResourceRequest( 185 resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), 186 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), 187 "VRead " + resourceClass.getName() + "/" + id + "/?_history/" + version, timeoutNormal); 188 if (result.isUnsuccessfulRequest()) { 189 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 190 (OperationOutcome) result.getPayload()); 191 } 192 } catch (Exception e) { 193 throw new FHIRException("Error trying to read this version of the resource", e); 194 } 195 return result.getPayload(); 196 } 197 198 public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) { 199 recordUse(); 200 ResourceRequest<T> result = null; 201 try { 202 result = client.issueGetResourceRequest( 203 resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL), 204 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "Read " + resourceClass.getName() + "?url=" + canonicalURL, 205 timeoutNormal); 206 if (result.isUnsuccessfulRequest()) { 207 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 208 (OperationOutcome) result.getPayload()); 209 } 210 } catch (Exception e) { 211 handleException(0, "An error has occurred while trying to read this version of the resource", e); 212 } 213 Bundle bnd = (Bundle) result.getPayload(); 214 if (bnd.getEntry().size() == 0) 215 throw new EFhirClientException("No matching resource found for canonical URL '" + canonicalURL + "'"); 216 if (bnd.getEntry().size() > 1) 217 throw new EFhirClientException("Multiple matching resources found for canonical URL '" + canonicalURL + "'"); 218 return (T) bnd.getEntry().get(0).getResource(); 219 } 220 221 public Resource update(Resource resource) { 222 recordUse(); 223 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 224 try { 225 result = client.issuePutRequest( 226 resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()), 227 ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), 228 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "Update " + resource.fhirType() + "/" + resource.getId(), 229 timeoutOperation); 230 if (result.isUnsuccessfulRequest()) { 231 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 232 (OperationOutcome) result.getPayload()); 233 } 234 } catch (Exception e) { 235 throw new EFhirClientException(0, "An error has occurred while trying to update this resource", e); 236 } 237 // TODO oe 26.1.2015 could be made nicer if only OperationOutcome locationheader 238 // is returned with an operationOutcome would be returned (and not the resource 239 // also) we make another read 240 try { 241 OperationOutcome operationOutcome = (OperationOutcome) result.getPayload(); 242 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress 243 .parseCreateLocation(result.getLocation()); 244 return this.vread(resource.getClass(), resVersionedIdentifier.getId(), resVersionedIdentifier.getVersionId()); 245 } catch (ClassCastException e) { 246 // if we fall throught we have the correct type already in the create 247 } 248 249 return result.getPayload(); 250 } 251 252 public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) { 253 recordUse(); 254 ResourceRequest<T> result = null; 255 try { 256 result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), 257 ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), 258 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "Update " + resource.fhirType() + "/" + id, 259 timeoutOperation); 260 if (result.isUnsuccessfulRequest()) { 261 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 262 (OperationOutcome) result.getPayload()); 263 } 264 } catch (Exception e) { 265 throw new EFhirClientException(0, "An error has occurred while trying to update this resource", e); 266 } 267 // TODO oe 26.1.2015 could be made nicer if only OperationOutcome locationheader 268 // is returned with an operationOutcome would be returned (and not the resource 269 // also) we make another read 270 try { 271 OperationOutcome operationOutcome = (OperationOutcome) result.getPayload(); 272 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress 273 .parseCreateLocation(result.getLocation()); 274 return this.vread(resourceClass, resVersionedIdentifier.getId(), resVersionedIdentifier.getVersionId()); 275 } catch (ClassCastException e) { 276 // if we fall through we have the correct type already in the create 277 } 278 279 return result.getPayload(); 280 } 281 282 public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) throws IOException { 283 recordUse(); 284 boolean complex = false; 285 for (ParametersParameterComponent p : params.getParameter()) 286 complex = complex || !(p.getValue() instanceof PrimitiveType); 287 String ps = ""; 288 if (!complex) 289 for (ParametersParameterComponent p : params.getParameter()) 290 if (p.getValue() instanceof PrimitiveType) 291 ps += Utilities.encodeUriParam(p.getName(), ((PrimitiveType) p.getValue()).asStringValue()) + "&"; 292 ResourceRequest<T> result; 293 URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps); 294 if (complex) { 295 byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true); 296 result = client.issuePostRequest(url, body, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), 297 "POST " + resourceClass.getName() + "/$" + name, timeoutLong); 298 } else { 299 result = client.issueGetResourceRequest(url, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), 300 "GET " + resourceClass.getName() + "/$" + name, timeoutLong); 301 } 302 if (result.isUnsuccessfulRequest()) { 303 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 304 (OperationOutcome) result.getPayload()); 305 } 306 if (result.getPayload() instanceof Parameters) { 307 return (Parameters) result.getPayload(); 308 } else { 309 Parameters p_out = new Parameters(); 310 p_out.addParameter().setName("return").setResource(result.getPayload()); 311 return p_out; 312 } 313 } 314 315 public Bundle transaction(Bundle batch) { 316 recordUse(); 317 Bundle transactionResult = null; 318 try { 319 transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), 320 ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat()), false), 321 withVer(getPreferredResourceFormat(), "4.0"), "transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size())); 322 } catch (Exception e) { 323 handleException(0, "An error occurred trying to process this transaction request", e); 324 } 325 return transactionResult; 326 } 327 328 @SuppressWarnings("unchecked") 329 public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) { 330 recordUse(); 331 ResourceRequest<T> result = null; 332 try { 333 result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id), 334 ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), 335 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), 336 "POST " + resourceClass.getName() + (id != null ? "/" + id : "") + "/$validate", timeoutLong); 337 if (result.isUnsuccessfulRequest()) { 338 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 339 (OperationOutcome) result.getPayload()); 340 } 341 } catch (Exception e) { 342 handleException(0, "An error has occurred while trying to validate this resource", e); 343 } 344 return (OperationOutcome) result.getPayload(); 345 } 346 347 @SuppressWarnings("unchecked") 348 public <T extends Resource> OperationOutcome validate(Resource resource, String id) { 349 recordUse(); 350 ResourceRequest<T> result = null; 351 try { 352 result = client.issuePostRequest(resourceAddress.resolveValidateUri(resource.fhirType(), id), 353 ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), 354 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), 355 "POST " + resource.fhirType() + (id != null ? "/" + id : "") + "/$validate", timeoutLong); 356 if (result.isUnsuccessfulRequest()) { 357 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 358 (OperationOutcome) result.getPayload()); 359 } 360 } catch (Exception e) { 361 handleException(0, "An error has occurred while trying to validate this resource", e); 362 } 363 return (OperationOutcome) result.getPayload(); 364 } 365 366 /** 367 * Helper method to prevent nesting of previously thrown EFhirClientExceptions. If the e param is an instance of 368 * EFhirClientException, it will be rethrown. Otherwise, a new EFhirClientException will be thrown with e as the 369 * cause. 370 * 371 * @param code The EFhirClientException code. 372 * @param message The EFhirClientException message. 373 * @param e The exception. 374 * @throws EFhirClientException representing the exception. 375 */ 376 protected void handleException(int code, String message, Exception e) throws EFhirClientException { 377 if (e instanceof EFhirClientException) { 378 throw (EFhirClientException) e; 379 } else { 380 throw new EFhirClientException(code, message, e); 381 } 382 } 383 384 /** 385 * Helper method to determine whether desired resource representation 386 * is Json or XML. 387 * 388 * @param format The format 389 * @return true if the format is JSON, false otherwise 390 */ 391 protected boolean isJson(String format) { 392 boolean isJson = false; 393 if (format.toLowerCase().contains("json")) { 394 isJson = true; 395 } 396 return isJson; 397 } 398 399 public Bundle fetchFeed(String url) { 400 recordUse(); 401 Bundle feed = null; 402 try { 403 feed = client.issueGetFeedRequest(new URI(url), withVer(getPreferredResourceFormat(), "4.0"), timeoutLong); 404 } catch (Exception e) { 405 handleException(0, "An error has occurred while trying to read a bundle", e); 406 } 407 return feed; 408 } 409 410 public Parameters lookupCode(Map<String, String> params) { 411 recordUse(); 412 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 413 try { 414 result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params), 415 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "CodeSystem/$lookup", timeoutNormal); 416 } catch (IOException e) { 417 throw new FHIRException(e); 418 } 419 if (result.isUnsuccessfulRequest()) { 420 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 421 (OperationOutcome) result.getPayload()); 422 } 423 return (Parameters) result.getPayload(); 424 } 425 426 public Parameters lookupCode(Parameters p) { 427 recordUse(); 428 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 429 try { 430 result = client.issuePostRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup"), 431 ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), 432 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "CodeSystem/$lookup", timeoutNormal); 433 } catch (IOException e) { 434 throw new FHIRException(e); 435 } 436 if (result.isUnsuccessfulRequest()) { 437 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 438 (OperationOutcome) result.getPayload()); 439 } 440 return (Parameters) result.getPayload(); 441 } 442 443 public Parameters translate(Parameters p) { 444 recordUse(); 445 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 446 try { 447 result = client.issuePostRequest(resourceAddress.resolveOperationUri(ConceptMap.class, "translate"), 448 ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), 449 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "ConceptMap/$translate", timeoutNormal); 450 } catch (IOException e) { 451 throw new FHIRException(e); 452 } 453 if (result.isUnsuccessfulRequest()) { 454 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 455 (OperationOutcome) result.getPayload()); 456 } 457 return (Parameters) result.getPayload(); 458 } 459 460 public ValueSet expandValueset(ValueSet source, Parameters expParams) { 461 recordUse(); 462 Parameters p = expParams == null ? new Parameters() : expParams.copy(); 463 if (source != null) { 464 p.addParameter().setName("valueSet").setResource(source); 465 } 466 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 467 try { 468 result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"), 469 ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), 470 generateHeaders(true), source == null ? "ValueSet/$expand" : "ValueSet/$expand?url=" + source.getUrl(), 471 timeoutExpand); 472 if (result.isUnsuccessfulRequest()) { 473 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 474 (OperationOutcome) result.getPayload()); 475 } 476 } catch (EFhirClientException e) { 477 if (e.getServerErrors().size() > 0) { 478 throw new EFhirClientException(e.getCode(), e.getMessage(), e.getServerErrors().get(0)); 479 } else { 480 throw new EFhirClientException(e.getCode(), e.getMessage(), e); 481 } 482 } catch (Exception e) { 483 throw new EFhirClientException(0, e.getMessage(), e); 484 } 485 return result == null ? null : (ValueSet) result.getPayload(); 486 } 487 488 public String getAddress() { 489 return base; 490 } 491 492 public ConceptMap initializeClosure(String name) { 493 recordUse(); 494 Parameters params = new Parameters(); 495 params.addParameter().setName("name").setValue(new StringType(name)); 496 ResourceRequest<Resource> result = null; 497 try { 498 result = client.issuePostRequest( 499 resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 500 ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true), 501 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "Closure?name=" + name, timeoutNormal); 502 if (result.isUnsuccessfulRequest()) { 503 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 504 (OperationOutcome) result.getPayload()); 505 } 506 } catch (IOException e) { 507 throw new FHIRException(e); 508 } 509 return result == null ? null : (ConceptMap) result.getPayload(); 510 } 511 512 public ConceptMap updateClosure(String name, Coding coding) { 513 recordUse(); 514 Parameters params = new Parameters(); 515 params.addParameter().setName("name").setValue(new StringType(name)); 516 params.addParameter().setName("concept").setValue(coding); 517 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 518 try { 519 result = client.issuePostRequest( 520 resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 521 ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true), 522 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "UpdateClosure?name=" + name, timeoutOperation); 523 if (result.isUnsuccessfulRequest()) { 524 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 525 (OperationOutcome) result.getPayload()); 526 } 527 } catch (IOException e) { 528 throw new FHIRException(e); 529 } 530 return result == null ? null : (ConceptMap) result.getPayload(); 531 } 532 533 public ToolingClientLogger getLogger() { 534 return client.getLogger(); 535 } 536 537 public void setLogger(ToolingClientLogger logger) { 538 client.setLogger(logger); 539 } 540 541 public int getRetryCount() { 542 return client.getRetryCount(); 543 } 544 545 public void setRetryCount(int retryCount) { 546 client.setRetryCount(retryCount); 547 } 548 549 public void setClientHeaders(Iterable<HTTPHeader> headers) { 550 this.headers = new ArrayList<>(); 551 headers.forEach(this.headers::add); 552 } 553 554 private Iterable<HTTPHeader> generateHeaders(boolean hasBody) { 555 // Add any other headers 556 List<HTTPHeader> headers = new ArrayList<>(this.headers); 557 if (!Utilities.noString(userAgent)) { 558 headers.add(new HTTPHeader("User-Agent",userAgent)); 559 } 560 561 if (!Utilities.noString(acceptLanguage)) { 562 headers.add(new HTTPHeader("Accept-Language", acceptLanguage)); 563 } 564 565 if (hasBody && !Utilities.noString(contentLanguage)) { 566 headers.add(new HTTPHeader("Content-Language",contentLanguage)); 567 } 568 569 return headers; 570 } 571 572 public String getServerVersion() { 573 return getCapabilitiesStatementQuick().getSoftware().getVersion(); 574 } 575 576 public Bundle search(String type, String criteria) { 577 recordUse(); 578 return fetchFeed(Utilities.pathURL(base, type+criteria)); 579 } 580 581 public <T extends Resource> T fetchResource(Class<T> resourceClass, String id) { 582 recordUse(); 583 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 584 try { 585 result = client.issueGetResourceRequest(resourceAddress.resolveGetResource(resourceClass, id), 586 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), resourceClass.getName()+"/"+id, timeoutNormal); 587 } catch (IOException e) { 588 throw new FHIRException(e); 589 } 590 if (result.isUnsuccessfulRequest()) { 591 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 592 (OperationOutcome) result.getPayload()); 593 } 594 return (T) result.getPayload(); 595 } 596 597 public int getUseCount() { 598 return useCount; 599 } 600 601 private void recordUse() { 602 useCount++; 603 } 604 605 public Parameters subsumes(Map<String, String> params) { 606 recordUse(); 607 org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null; 608 try { 609 result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "subsumes", params), 610 withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "CodeSystem/$subsumes", timeoutNormal); 611 } catch (IOException e) { 612 throw new FHIRException(e); 613 } 614 if (result.isUnsuccessfulRequest()) { 615 throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), 616 (OperationOutcome) result.getPayload()); 617 } 618 return (Parameters) result.getPayload(); 619 } 620 621 622}