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