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