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