001package org.hl7.fhir.r4.utils.client; 002 003 004 005 006 007/* 008 Copyright (c) 2011+, HL7, Inc. 009 All rights reserved. 010 011 Redistribution and use in source and binary forms, with or without modification, 012 are permitted provided that the following conditions are met: 013 014 * Redistributions of source code must retain the above copyright notice, this 015 list of conditions and the following disclaimer. 016 * Redistributions in binary form must reproduce the above copyright notice, 017 this list of conditions and the following disclaimer in the documentation 018 and/or other materials provided with the distribution. 019 * Neither the name of HL7 nor the names of its contributors may be used to 020 endorse or promote products derived from this software without specific 021 prior written permission. 022 023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 024 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 026 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 027 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 028 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 029 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 030 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 031 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 032 POSSIBILITY OF SUCH DAMAGE. 033 034*/ 035 036import java.net.URI; 037import java.net.URISyntaxException; 038import java.util.HashMap; 039import java.util.List; 040import java.util.Map; 041 042import org.apache.http.Header; 043import org.apache.http.HttpHost; 044import org.hl7.fhir.r4.model.Bundle; 045import org.hl7.fhir.r4.model.CapabilityStatement; 046import org.hl7.fhir.r4.model.CodeSystem; 047import org.hl7.fhir.r4.model.Coding; 048import org.hl7.fhir.r4.model.ConceptMap; 049import org.hl7.fhir.r4.model.OperationOutcome; 050import org.hl7.fhir.r4.model.Parameters; 051import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; 052import org.hl7.fhir.r4.model.PrimitiveType; 053import org.hl7.fhir.r4.model.Resource; 054import org.hl7.fhir.r4.model.StringType; 055import org.hl7.fhir.r4.model.TerminologyCapabilities; 056import org.hl7.fhir.r4.model.ValueSet; 057import org.hl7.fhir.utilities.ToolingClientLogger; 058import org.hl7.fhir.utilities.Utilities; 059 060/** 061 * Very Simple RESTful client. This is purely for use in the standalone 062 * tools jar packages. It doesn't support many features, only what the tools 063 * need. 064 * 065 * To use, initialize class and set base service URI as follows: 066 * 067 * <pre><code> 068 * FHIRSimpleClient fhirClient = new FHIRSimpleClient(); 069 * fhirClient.initialize("http://my.fhir.domain/myServiceRoot"); 070 * </code></pre> 071 * 072 * Default Accept and Content-Type headers are application/fhir+xml and application/fhir+json. 073 * 074 * These can be changed by invoking the following setter functions: 075 * 076 * <pre><code> 077 * setPreferredResourceFormat() 078 * setPreferredFeedFormat() 079 * </code></pre> 080 * 081 * TODO Review all sad paths. 082 * 083 * @author Claude Nanjo 084 * 085 */ 086public class FHIRToolingClient { 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 private static final int TIMEOUT_NORMAL = 15; 093 private static final int TIMEOUT_OPERATION = 30; 094 private static final int TIMEOUT_OPERATION_LONG = 60; 095 private static final int TIMEOUT_OPERATION_EXPAND = 120; 096 097 private String base; 098 private ResourceAddress resourceAddress; 099 private ResourceFormat preferredResourceFormat; 100 private int maxResultSetSize = -1;//_count 101 private CapabilityStatement capabilities; 102 103 private ClientUtils utils = new ClientUtils(); 104 105 //Pass enpoint for client - URI 106 public FHIRToolingClient(String baseServiceUrl) throws URISyntaxException { 107 preferredResourceFormat = ResourceFormat.RESOURCE_XML; 108 detectProxy(); 109 initialize(baseServiceUrl); 110 } 111 112 public FHIRToolingClient(String baseServiceUrl, String username, String password) throws URISyntaxException { 113 preferredResourceFormat = ResourceFormat.RESOURCE_XML; 114 utils.setUsername(username); 115 utils.setPassword(password); 116 detectProxy(); 117 initialize(baseServiceUrl); 118 } 119 120 public void configureProxy(String proxyHost, int proxyPort) { 121 utils.setProxy(new HttpHost(proxyHost, proxyPort)); 122 } 123 124 public void detectProxy() { 125 String host = System.getenv(hostKey); 126 String port = System.getenv(portKey); 127 128 if(host==null) { 129 host = System.getProperty(hostKey); 130 } 131 132 if(port==null) { 133 port = System.getProperty(portKey); 134 } 135 136 if(host!=null && port!=null) { 137 this.configureProxy(host, Integer.parseInt(port)); 138 } 139 } 140 141 public void initialize(String baseServiceUrl) throws URISyntaxException { 142 base = baseServiceUrl; 143 resourceAddress = new ResourceAddress(baseServiceUrl); 144 this.maxResultSetSize = -1; 145 checkCapabilities(); 146 } 147 148 private void checkCapabilities() { 149 try { 150 capabilities = getCapabilitiesStatementQuick(); 151 } catch (Throwable e) { 152 } 153 } 154 155 public String getPreferredResourceFormat() { 156 return preferredResourceFormat.getHeader(); 157 } 158 159 public void setPreferredResourceFormat(ResourceFormat resourceFormat) { 160 preferredResourceFormat = resourceFormat; 161 } 162 163 public int getMaximumRecordCount() { 164 return maxResultSetSize; 165 } 166 167 public void setMaximumRecordCount(int maxResultSetSize) { 168 this.maxResultSetSize = maxResultSetSize; 169 } 170 171 public TerminologyCapabilities getTerminologyCapabilities() { 172 return (TerminologyCapabilities) utils.issueGetResourceRequest(resourceAddress.resolveMetadataTxCaps(), getPreferredResourceFormat(), "TerminologyCapabilities", TIMEOUT_NORMAL).getReference(); 173 } 174 175 public CapabilityStatement getCapabilitiesStatement() { 176 CapabilityStatement conformance = null; 177 try { 178 conformance = (CapabilityStatement)utils.issueGetResourceRequest(resourceAddress.resolveMetadataUri(false), getPreferredResourceFormat(), "CapabilitiesStatement", TIMEOUT_NORMAL).getReference(); 179 } catch(Exception e) { 180 handleException("An error has occurred while trying to fetch the server's conformance statement", e); 181 } 182 return conformance; 183 } 184 185 public CapabilityStatement getCapabilitiesStatementQuick() throws EFhirClientException { 186 if (capabilities != null) 187 return capabilities; 188 try { 189 capabilities = (CapabilityStatement)utils.issueGetResourceRequest(resourceAddress.resolveMetadataUri(true), getPreferredResourceFormat(), "CapabilitiesStatement-Quick", TIMEOUT_NORMAL).getReference(); 190 } catch(Exception e) { 191 handleException("An error has occurred while trying to fetch the server's conformance statement", e); 192 } 193 return capabilities; 194 } 195 196 public <T extends Resource> T read(Class<T> resourceClass, String id) {//TODO Change this to AddressableResource 197 ResourceRequest<T> result = null; 198 try { 199 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), getPreferredResourceFormat(), "Read "+resourceClass.getName()+"/"+id, TIMEOUT_NORMAL); 200 result.addErrorStatus(410);//gone 201 result.addErrorStatus(404);//unknown 202 result.addSuccessStatus(200);//Only one for now 203 if(result.isUnsuccessfulRequest()) { 204 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 205 } 206 } catch (Exception e) { 207 handleException("An error has occurred while trying to read this resource", e); 208 } 209 return result.getPayload(); 210 } 211 212 public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) { 213 ResourceRequest<T> result = null; 214 try { 215 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), getPreferredResourceFormat(), "VRead "+resourceClass.getName()+"/"+id+"/?_history/"+version, TIMEOUT_NORMAL); 216 result.addErrorStatus(410);//gone 217 result.addErrorStatus(404);//unknown 218 result.addErrorStatus(405);//unknown 219 result.addSuccessStatus(200);//Only one for now 220 if(result.isUnsuccessfulRequest()) { 221 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 222 } 223 } catch (Exception e) { 224 handleException("An error has occurred while trying to read this version of the resource", e); 225 } 226 return result.getPayload(); 227 } 228 229 // GET fhir/ValueSet?url=http://hl7.org/fhir/ValueSet/clinical-findings&version=0.8 230 231 public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) { 232 ResourceRequest<T> result = null; 233 try { 234 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL), getPreferredResourceFormat(), "Read "+resourceClass.getName()+"?url="+canonicalURL, TIMEOUT_NORMAL); 235 result.addErrorStatus(410);//gone 236 result.addErrorStatus(404);//unknown 237 result.addErrorStatus(405);//unknown 238 result.addSuccessStatus(200);//Only one for now 239 if(result.isUnsuccessfulRequest()) { 240 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 241 } 242 } catch (Exception e) { 243 handleException("An error has occurred while trying to read this version of the resource", e); 244 } 245 Bundle bnd = (Bundle) result.getPayload(); 246 if (bnd.getEntry().size() == 0) 247 throw new EFhirClientException("No matching resource found for canonical URL '"+canonicalURL+"'"); 248 if (bnd.getEntry().size() > 1) 249 throw new EFhirClientException("Multiple matching resources found for canonical URL '"+canonicalURL+"'"); 250 return (T) bnd.getEntry().get(0).getResource(); 251 } 252 253 254 public Resource update(Resource resource) { 255 ResourceRequest<Resource> result = null; 256 try { 257 List<Header> headers = null; 258 result = utils.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, 259 "Update "+resource.fhirType()+"/"+resource.getId(), TIMEOUT_OPERATION); 260 result.addErrorStatus(410);//gone 261 result.addErrorStatus(404);//unknown 262 result.addErrorStatus(405); 263 result.addErrorStatus(422);//Unprocessable Entity 264 result.addSuccessStatus(200); 265 result.addSuccessStatus(201); 266 if(result.isUnsuccessfulRequest()) { 267 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 268 } 269 } catch(Exception e) { 270 throw new EFhirClientException("An error has occurred while trying to update this resource", e); 271 } 272 // 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 273 try { 274 OperationOutcome operationOutcome = (OperationOutcome)result.getPayload(); 275 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress.parseCreateLocation(result.getLocation()); 276 return this.vread(resource.getClass(), resVersionedIdentifier.getId(),resVersionedIdentifier.getVersionId()); 277 } catch(ClassCastException e) { 278 // if we fall throught we have the correct type already in the create 279 } 280 281 return result.getPayload(); 282 } 283 284 public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) { 285 ResourceRequest<T> result = null; 286 try { 287 List<Header> headers = null; 288 result = utils.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, 289 "Update "+resource.fhirType()+"/"+id, TIMEOUT_OPERATION); 290 result.addErrorStatus(410);//gone 291 result.addErrorStatus(404);//unknown 292 result.addErrorStatus(405); 293 result.addErrorStatus(422);//Unprocessable Entity 294 result.addSuccessStatus(200); 295 result.addSuccessStatus(201); 296 if(result.isUnsuccessfulRequest()) { 297 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 298 } 299 } catch(Exception e) { 300 throw new EFhirClientException("An error has occurred while trying to update this resource", e); 301 } 302 // 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 303 try { 304 OperationOutcome operationOutcome = (OperationOutcome)result.getPayload(); 305 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress.parseCreateLocation(result.getLocation()); 306 return this.vread(resourceClass, resVersionedIdentifier.getId(),resVersionedIdentifier.getVersionId()); 307 } catch(ClassCastException e) { 308 // if we fall throught we have the correct type already in the create 309 } 310 311 return result.getPayload(); 312 } 313 314// 315// public <T extends Resource> boolean delete(Class<T> resourceClass, String id) { 316// try { 317// return utils.issueDeleteRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), proxy); 318// } catch(Exception e) { 319// throw new EFhirClientException("An error has occurred while trying to delete this resource", e); 320// } 321// 322// } 323 324// 325// public <T extends Resource> OperationOutcome create(Class<T> resourceClass, T resource) { 326// ResourceRequest<T> resourceRequest = null; 327// try { 328// List<Header> headers = null; 329// resourceRequest = utils.issuePostRequest(resourceAddress.resolveGetUriFromResourceClass(resourceClass),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, proxy); 330// resourceRequest.addSuccessStatus(201); 331// if(resourceRequest.isUnsuccessfulRequest()) { 332// throw new EFhirClientException("Server responded with HTTP error code " + resourceRequest.getHttpStatus(), (OperationOutcome)resourceRequest.getPayload()); 333// } 334// } catch(Exception e) { 335// handleException("An error has occurred while trying to create this resource", e); 336// } 337// OperationOutcome operationOutcome = null;; 338// try { 339// operationOutcome = (OperationOutcome)resourceRequest.getPayload(); 340// ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = 341// ResourceAddress.parseCreateLocation(resourceRequest.getLocation()); 342// OperationOutcomeIssueComponent issue = operationOutcome.addIssue(); 343// issue.setSeverity(IssueSeverity.INFORMATION); 344// issue.setUserData(ResourceAddress.ResourceVersionedIdentifier.class.toString(), 345// resVersionedIdentifier); 346// return operationOutcome; 347// } catch(ClassCastException e) { 348// // some server (e.g. grahams) returns the resource directly 349// operationOutcome = new OperationOutcome(); 350// OperationOutcomeIssueComponent issue = operationOutcome.addIssue(); 351// issue.setSeverity(IssueSeverity.INFORMATION); 352// issue.setUserData(ResourceRequest.class.toString(), 353// resourceRequest.getPayload()); 354// return operationOutcome; 355// } 356// } 357 358// 359// public <T extends Resource> Bundle history(Calendar lastUpdate, Class<T> resourceClass, String id) { 360// Bundle history = null; 361// try { 362// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 363// } catch (Exception e) { 364// handleException("An error has occurred while trying to retrieve history information for this resource", e); 365// } 366// return history; 367// } 368 369// 370// public <T extends Resource> Bundle history(Date lastUpdate, Class<T> resourceClass, String id) { 371// Bundle history = null; 372// try { 373// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 374// } catch (Exception e) { 375// handleException("An error has occurred while trying to retrieve history information for this resource", e); 376// } 377// return history; 378// } 379// 380// 381// public <T extends Resource> Bundle history(Calendar lastUpdate, Class<T> resourceClass) { 382// Bundle history = null; 383// try { 384// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 385// } catch (Exception e) { 386// handleException("An error has occurred while trying to retrieve history information for this resource type", e); 387// } 388// return history; 389// } 390// 391// 392// public <T extends Resource> Bundle history(Date lastUpdate, Class<T> resourceClass) { 393// Bundle history = null; 394// try { 395// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 396// } catch (Exception e) { 397// handleException("An error has occurred while trying to retrieve history information for this resource type", e); 398// } 399// return history; 400// } 401// 402// 403// public <T extends Resource> Bundle history(Class<T> resourceClass) { 404// Bundle history = null; 405// try { 406// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, maxResultSetSize), getPreferredResourceFormat(), proxy); 407// } catch (Exception e) { 408// handleException("An error has occurred while trying to retrieve history information for this resource type", e); 409// } 410// return history; 411// } 412// 413// 414// public <T extends Resource> Bundle history(Class<T> resourceClass, String id) { 415// Bundle history = null; 416// try { 417// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, maxResultSetSize), getPreferredResourceFormat(), proxy); 418// } catch (Exception e) { 419// handleException("An error has occurred while trying to retrieve history information for this resource", e); 420// } 421// return history; 422// } 423// 424// 425// public <T extends Resource> Bundle history(Date lastUpdate) { 426// Bundle history = null; 427// try { 428// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 429// } catch (Exception e) { 430// handleException("An error has occurred while trying to retrieve history since last update",e); 431// } 432// return history; 433// } 434// 435// 436// public <T extends Resource> Bundle history(Calendar lastUpdate) { 437// Bundle history = null; 438// try { 439// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 440// } catch (Exception e) { 441// handleException("An error has occurred while trying to retrieve history since last update",e); 442// } 443// return history; 444// } 445// 446// 447// public <T extends Resource> Bundle history() { 448// Bundle history = null; 449// try { 450// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(maxResultSetSize), getPreferredResourceFormat(), proxy); 451// } catch (Exception e) { 452// handleException("An error has occurred while trying to retrieve history since last update",e); 453// } 454// return history; 455// } 456// 457// 458// public <T extends Resource> Bundle search(Class<T> resourceClass, Map<String, String> parameters) { 459// Bundle searchResults = null; 460// try { 461// searchResults = utils.issueGetFeedRequest(resourceAddress.resolveSearchUri(resourceClass, parameters), getPreferredResourceFormat(), proxy); 462// } catch (Exception e) { 463// handleException("Error performing search with parameters " + parameters, e); 464// } 465// return searchResults; 466// } 467// 468// 469// public <T extends Resource> Bundle searchPost(Class<T> resourceClass, T resource, Map<String, String> parameters) { 470// Bundle searchResults = null; 471// try { 472// searchResults = utils.issuePostFeedRequest(resourceAddress.resolveSearchUri(resourceClass, new HashMap<String, String>()), parameters, "src", resource, getPreferredResourceFormat()); 473// } catch (Exception e) { 474// handleException("Error performing search with parameters " + parameters, e); 475// } 476// return searchResults; 477// } 478 479 480 public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) { 481 boolean complex = false; 482 for (ParametersParameterComponent p : params.getParameter()) 483 complex = complex || !(p.getValue() instanceof PrimitiveType); 484 Parameters searchResults = null; 485 String ps = ""; 486 try { 487 if (!complex) 488 for (ParametersParameterComponent p : params.getParameter()) 489 if (p.getValue() instanceof PrimitiveType) 490 ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue())+"&"; 491 ResourceRequest<T> result; 492 if (complex) 493 result = utils.issuePostRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), 494 "POST "+resourceClass.getName()+"/$"+name, TIMEOUT_OPERATION_LONG); 495 else 496 result = utils.issueGetResourceRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), getPreferredResourceFormat(), "GET "+resourceClass.getName()+"/$"+name, TIMEOUT_OPERATION_LONG); 497 result.addErrorStatus(410);//gone 498 result.addErrorStatus(404);//unknown 499 result.addSuccessStatus(200);//Only one for now 500 if(result.isUnsuccessfulRequest()) 501 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 502 if (result.getPayload() instanceof Parameters) 503 return (Parameters) result.getPayload(); 504 else { 505 Parameters p_out = new Parameters(); 506 p_out.addParameter().setName("return").setResource(result.getPayload()); 507 return p_out; 508 } 509 } catch (Exception e) { 510 handleException("Error performing operation '"+name+"' with parameters " + ps, e); 511 } 512 return null; 513 } 514 515 516 public Bundle transaction(Bundle batch) { 517 Bundle transactionResult = null; 518 try { 519 transactionResult = utils.postBatchRequest(resourceAddress.getBaseServiceUri(), utils.getFeedAsByteArray(batch, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), "transaction", TIMEOUT_NORMAL+batch.getEntry().size()); 520 } catch (Exception e) { 521 handleException("An error occurred trying to process this transaction request", e); 522 } 523 return transactionResult; 524 } 525 526 @SuppressWarnings("unchecked") 527 public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) { 528 ResourceRequest<T> result = null; 529 try { 530 result = utils.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id), utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), "POST "+resourceClass.getName()+(id != null ? "/"+id : "")+"/$validate", TIMEOUT_OPERATION_LONG); 531 result.addErrorStatus(400);//gone 532 result.addErrorStatus(422);//Unprocessable Entity 533 result.addSuccessStatus(200);//OK 534 if(result.isUnsuccessfulRequest()) { 535 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 536 } 537 } catch(Exception e) { 538 handleException("An error has occurred while trying to validate this resource", e); 539 } 540 return (OperationOutcome)result.getPayload(); 541 } 542 543 /* change to meta operations 544 545 public List<Coding> getAllTags() { 546 TagListRequest result = null; 547 try { 548 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetAllTags(), getPreferredResourceFormat(), null, proxy); 549 } catch (Exception e) { 550 handleException("An error has occurred while trying to retrieve all tags", e); 551 } 552 return result.getPayload(); 553 } 554 555 556 public <T extends Resource> List<Coding> getAllTagsForResourceType(Class<T> resourceClass) { 557 TagListRequest result = null; 558 try { 559 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetAllTagsForResourceType(resourceClass), getPreferredResourceFormat(), null, proxy); 560 } catch (Exception e) { 561 handleException("An error has occurred while trying to retrieve tags for this resource type", e); 562 } 563 return result.getPayload(); 564 } 565 566 567 public <T extends Resource> List<Coding> getTagsForReference(Class<T> resource, String id) { 568 TagListRequest result = null; 569 try { 570 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetTagsForReference(resource, id), getPreferredResourceFormat(), null, proxy); 571 } catch (Exception e) { 572 handleException("An error has occurred while trying to retrieve tags for this resource", e); 573 } 574 return result.getPayload(); 575 } 576 577 578 public <T extends Resource> List<Coding> getTagsForResourceVersion(Class<T> resource, String id, String versionId) { 579 TagListRequest result = null; 580 try { 581 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetTagsForResourceVersion(resource, id, versionId), getPreferredResourceFormat(), null, proxy); 582 } catch (Exception e) { 583 handleException("An error has occurred while trying to retrieve tags for this resource version", e); 584 } 585 return result.getPayload(); 586 } 587 588// 589// public <T extends Resource> boolean deleteTagsForReference(Class<T> resourceClass, String id) { 590// try { 591// return utils.issueDeleteRequest(resourceAddress.resolveGetTagsForReference(resourceClass, id), proxy); 592// } catch(Exception e) { 593// handleException("An error has occurred while trying to retrieve tags for this resource version", e); 594// throw new EFhirClientException("An error has occurred while trying to delete this resource", e); 595// } 596// 597// } 598// 599// 600// public <T extends Resource> boolean deleteTagsForResourceVersion(Class<T> resourceClass, String id, List<Coding> tags, String version) { 601// try { 602// return utils.issueDeleteRequest(resourceAddress.resolveGetTagsForResourceVersion(resourceClass, id, version), proxy); 603// } catch(Exception e) { 604// handleException("An error has occurred while trying to retrieve tags for this resource version", e); 605// throw new EFhirClientException("An error has occurred while trying to delete this resource", e); 606// } 607// } 608 609 610 public <T extends Resource> List<Coding> createTags(List<Coding> tags, Class<T> resourceClass, String id) { 611 TagListRequest request = null; 612 try { 613 request = utils.issuePostRequestForTagList(resourceAddress.resolveGetTagsForReference(resourceClass, id),utils.getTagListAsByteArray(tags, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), null, proxy); 614 request.addSuccessStatus(201); 615 request.addSuccessStatus(200); 616 if(request.isUnsuccessfulRequest()) { 617 throw new EFhirClientException("Server responded with HTTP error code " + request.getHttpStatus()); 618 } 619 } catch(Exception e) { 620 handleException("An error has occurred while trying to set tags for this resource", e); 621 } 622 return request.getPayload(); 623 } 624 625 626 public <T extends Resource> List<Coding> createTags(List<Coding> tags, Class<T> resourceClass, String id, String version) { 627 TagListRequest request = null; 628 try { 629 request = utils.issuePostRequestForTagList(resourceAddress.resolveGetTagsForResourceVersion(resourceClass, id, version),utils.getTagListAsByteArray(tags, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), null, proxy); 630 request.addSuccessStatus(201); 631 request.addSuccessStatus(200); 632 if(request.isUnsuccessfulRequest()) { 633 throw new EFhirClientException("Server responded with HTTP error code " + request.getHttpStatus()); 634 } 635 } catch(Exception e) { 636 handleException("An error has occurred while trying to set the tags for this resource version", e); 637 } 638 return request.getPayload(); 639 } 640 641 642 public <T extends Resource> List<Coding> deleteTags(List<Coding> tags, Class<T> resourceClass, String id, String version) { 643 TagListRequest request = null; 644 try { 645 request = utils.issuePostRequestForTagList(resourceAddress.resolveDeleteTagsForResourceVersion(resourceClass, id, version),utils.getTagListAsByteArray(tags, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), null, proxy); 646 request.addSuccessStatus(201); 647 request.addSuccessStatus(200); 648 if(request.isUnsuccessfulRequest()) { 649 throw new EFhirClientException("Server responded with HTTP error code " + request.getHttpStatus()); 650 } 651 } catch(Exception e) { 652 handleException("An error has occurred while trying to delete the tags for this resource version", e); 653 } 654 return request.getPayload(); 655 } 656 */ 657 658 /** 659 * Helper method to prevent nesting of previously thrown EFhirClientExceptions 660 * 661 * @param e 662 * @throws EFhirClientException 663 */ 664 protected void handleException(String message, Exception e) throws EFhirClientException { 665 if(e instanceof EFhirClientException) { 666 throw (EFhirClientException)e; 667 } else { 668 throw new EFhirClientException(message, e); 669 } 670 } 671 672 /** 673 * Helper method to determine whether desired resource representation 674 * is Json or XML. 675 * 676 * @param format 677 * @return 678 */ 679 protected boolean isJson(String format) { 680 boolean isJson = false; 681 if(format.toLowerCase().contains("json")) { 682 isJson = true; 683 } 684 return isJson; 685 } 686 687 public Bundle fetchFeed(String url) { 688 Bundle feed = null; 689 try { 690 feed = utils.issueGetFeedRequest(new URI(url), getPreferredResourceFormat()); 691 } catch (Exception e) { 692 handleException("An error has occurred while trying to retrieve history since last update",e); 693 } 694 return feed; 695 } 696 697 public ValueSet expandValueset(ValueSet source, Parameters expParams) { 698 List<Header> headers = null; 699 Parameters p = expParams == null ? new Parameters() : expParams.copy(); 700 p.addParameter().setName("valueSet").setResource(source); 701 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"), 702 utils.getResourceAsByteArray(p, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, "ValueSet/$expand?url="+source.getUrl(), TIMEOUT_OPERATION_EXPAND); 703 result.addErrorStatus(410);//gone 704 result.addErrorStatus(404);//unknown 705 result.addErrorStatus(405); 706 result.addErrorStatus(422);//Unprocessable Entity 707 result.addSuccessStatus(200); 708 result.addSuccessStatus(201); 709 if(result.isUnsuccessfulRequest()) { 710 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 711 } 712 return (ValueSet) result.getPayload(); 713 } 714 715 716 public Parameters lookupCode(Map<String, String> params) { 717 ResourceRequest<Resource> result = utils.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params), getPreferredResourceFormat(), "CodeSystem/$lookup", TIMEOUT_NORMAL); 718 result.addErrorStatus(410);//gone 719 result.addErrorStatus(404);//unknown 720 result.addErrorStatus(405); 721 result.addErrorStatus(422);//Unprocessable Entity 722 result.addSuccessStatus(200); 723 result.addSuccessStatus(201); 724 if(result.isUnsuccessfulRequest()) { 725 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 726 } 727 return (Parameters) result.getPayload(); 728 } 729 public ValueSet expandValueset(ValueSet source, Parameters expParams, Map<String, String> params) { 730 List<Header> headers = null; 731 Parameters p = expParams == null ? new Parameters() : expParams.copy(); 732 p.addParameter().setName("valueSet").setResource(source); 733 for (String n : params.keySet()) 734 p.addParameter().setName(n).setValue(new StringType(params.get(n))); 735 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", params), 736 utils.getResourceAsByteArray(p, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, "ValueSet/$expand?url="+source.getUrl(), TIMEOUT_OPERATION_EXPAND); 737 result.addErrorStatus(410);//gone 738 result.addErrorStatus(404);//unknown 739 result.addErrorStatus(405); 740 result.addErrorStatus(422);//Unprocessable Entity 741 result.addSuccessStatus(200); 742 result.addSuccessStatus(201); 743 if(result.isUnsuccessfulRequest()) { 744 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 745 } 746 return (ValueSet) result.getPayload(); 747 } 748 749// public ValueSet expandValueset(ValueSet source, ExpansionProfile profile, Map<String, String> params) { 750// List<Header> headers = null; 751// ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", params), 752// utils.getResourceAsByteArray(source, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, proxy); 753// result.addErrorStatus(410);//gone 754// result.addErrorStatus(404);//unknown 755// result.addErrorStatus(405); 756// result.addErrorStatus(422);//Unprocessable Entity 757// result.addSuccessStatus(200); 758// result.addSuccessStatus(201); 759// if(result.isUnsuccessfulRequest()) { 760// throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 761// } 762// return (ValueSet) result.getPayload(); 763// } 764 765 766 public String getAddress() { 767 return base; 768 } 769 770 public ConceptMap initializeClosure(String name) { 771 Parameters params = new Parameters(); 772 params.addParameter().setName("name").setValue(new StringType(name)); 773 List<Header> headers = null; 774 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 775 utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, "Closure?name="+name, TIMEOUT_NORMAL); 776 result.addErrorStatus(410);//gone 777 result.addErrorStatus(404);//unknown 778 result.addErrorStatus(405); 779 result.addErrorStatus(422);//Unprocessable Entity 780 result.addSuccessStatus(200); 781 result.addSuccessStatus(201); 782 if(result.isUnsuccessfulRequest()) { 783 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 784 } 785 return (ConceptMap) result.getPayload(); 786 } 787 788 public ConceptMap updateClosure(String name, Coding coding) { 789 Parameters params = new Parameters(); 790 params.addParameter().setName("name").setValue(new StringType(name)); 791 params.addParameter().setName("concept").setValue(coding); 792 List<Header> headers = null; 793 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 794 utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, "UpdateClosure?name="+name, TIMEOUT_OPERATION); 795 result.addErrorStatus(410);//gone 796 result.addErrorStatus(404);//unknown 797 result.addErrorStatus(405); 798 result.addErrorStatus(422);//Unprocessable Entity 799 result.addSuccessStatus(200); 800 result.addSuccessStatus(201); 801 if(result.isUnsuccessfulRequest()) { 802 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 803 } 804 return (ConceptMap) result.getPayload(); 805 } 806 807 public int getTimeout() { 808 return utils.getTimeout(); 809 } 810 811 public void setTimeout(int timeout) { 812 utils.setTimeout(timeout); 813 } 814 815 public String getUsername() { 816 return utils.getUsername(); 817 } 818 819 public void setUsername(String username) { 820 utils.setUsername(username); 821 } 822 823 public String getPassword() { 824 return utils.getPassword(); 825 } 826 827 public void setPassword(String password) { 828 utils.setPassword(password); 829 } 830 831 public ToolingClientLogger getLogger() { 832 return utils.getLogger(); 833 } 834 835 public void setLogger(ToolingClientLogger logger) { 836 utils.setLogger(logger); 837 } 838 839 public int getRetryCount() { 840 return utils.getRetryCount(); 841 } 842 843 public void setRetryCount(int retryCount) { 844 utils.setRetryCount(retryCount); 845 } 846 847 848}