001package org.hl7.fhir.dstu2016may.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.dstu2016may.model.Bundle; 045import org.hl7.fhir.dstu2016may.model.CodeSystem; 046import org.hl7.fhir.dstu2016may.model.Coding; 047import org.hl7.fhir.dstu2016may.model.ConceptMap; 048import org.hl7.fhir.dstu2016may.model.Conformance; 049import org.hl7.fhir.dstu2016may.model.OperationOutcome; 050import org.hl7.fhir.dstu2016may.model.Parameters; 051import org.hl7.fhir.dstu2016may.model.Parameters.ParametersParameterComponent; 052import org.hl7.fhir.dstu2016may.model.PrimitiveType; 053import org.hl7.fhir.dstu2016may.model.Resource; 054import org.hl7.fhir.dstu2016may.model.StringType; 055import org.hl7.fhir.dstu2016may.model.ValueSet; 056import org.hl7.fhir.utilities.ToolingClientLogger; 057import org.hl7.fhir.utilities.Utilities; 058 059/** 060 * Very Simple RESTful client. This is purely for use in the standalone 061 * tools jar packages. It doesn't support many features, only what the tools 062 * need. 063 * 064 * To use, initialize class and set base service URI as follows: 065 * 066 * <pre><code> 067 * FHIRSimpleClient fhirClient = new FHIRSimpleClient(); 068 * fhirClient.initialize("http://my.fhir.domain/myServiceRoot"); 069 * </code></pre> 070 * 071 * Default Accept and Content-Type headers are application/xml+fhir and application/j+fhir. 072 * 073 * These can be changed by invoking the following setter functions: 074 * 075 * <pre><code> 076 * setPreferredResourceFormat() 077 * setPreferredFeedFormat() 078 * </code></pre> 079 * 080 * TODO Review all sad paths. 081 * 082 * @author Claude Nanjo 083 * 084 */ 085public class FHIRToolingClient { 086 087 public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssK"; 088 public static final String DATE_FORMAT = "yyyy-MM-dd"; 089 public static final String hostKey = "http.proxyHost"; 090 public static final String portKey = "http.proxyPort"; 091 private static final int TIMEOUT_NORMAL = 1; 092 private static final int TIMEOUT_OPERATION = 2; 093 private static final int TIMEOUT_OPERATION_LONG = 3; 094 095 private String base; 096 private ResourceAddress resourceAddress; 097 private ResourceFormat preferredResourceFormat; 098 private HttpHost proxy; 099 private int maxResultSetSize = -1;//_count 100 private Conformance conf; 101 private ClientUtils utils = new ClientUtils(); 102 103 //Pass enpoint for client - URI 104 public FHIRToolingClient(String baseServiceUrl) throws URISyntaxException { 105 preferredResourceFormat = ResourceFormat.RESOURCE_XML; 106 detectProxy(); 107 initialize(baseServiceUrl); 108 } 109 110 public FHIRToolingClient(String baseServiceUrl, String username, String password) throws URISyntaxException { 111 preferredResourceFormat = ResourceFormat.RESOURCE_XML; 112 utils.setUsername(username); 113 utils.setPassword(password); 114 detectProxy(); 115 initialize(baseServiceUrl); 116 } 117 118 public void configureProxy(String proxyHost, int proxyPort) { 119 utils.setProxy(new HttpHost(proxyHost, proxyPort)); 120 } 121 122 public void detectProxy() { 123 String host = System.getenv(hostKey); 124 String port = System.getenv(portKey); 125 126 if(host==null) { 127 host = System.getProperty(hostKey); 128 } 129 130 if(port==null) { 131 port = System.getProperty(portKey); 132 } 133 134 if(host!=null && port!=null) { 135 this.configureProxy(host, Integer.parseInt(port)); 136 } 137 } 138 139 public void initialize(String baseServiceUrl) throws URISyntaxException { 140 base = baseServiceUrl; 141 resourceAddress = new ResourceAddress(baseServiceUrl); 142 this.maxResultSetSize = -1; 143 checkConformance(); 144 } 145 146 private void checkConformance() { 147 try { 148 conf = getConformanceStatementQuick(); 149 } catch (Throwable e) { 150 } 151 } 152 153 public String getPreferredResourceFormat() { 154 return preferredResourceFormat.getHeader(); 155 } 156 157 public void setPreferredResourceFormat(ResourceFormat resourceFormat) { 158 preferredResourceFormat = resourceFormat; 159 } 160 161 public int getMaximumRecordCount() { 162 return maxResultSetSize; 163 } 164 165 public void setMaximumRecordCount(int maxResultSetSize) { 166 this.maxResultSetSize = maxResultSetSize; 167 } 168 169 public Conformance getConformanceStatement() throws EFhirClientException { 170 if (conf != null) 171 return conf; 172 return getConformanceStatement(false); 173 } 174 175 public Conformance getConformanceStatement(boolean useOptionsVerb) { 176 Conformance conformance = null; 177 try { 178 if(useOptionsVerb) { 179 conformance = (Conformance)utils.issueOptionsRequest(resourceAddress.getBaseServiceUri(), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference();//TODO fix this 180 } else { 181 conformance = (Conformance)utils.issueGetResourceRequest(resourceAddress.resolveMetadataUri(false), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference(); 182 } 183 } catch(Exception e) { 184 handleException("An error has occurred while trying to fetch the server's conformance statement", e); 185 } 186 return conformance; 187 } 188 189 public Conformance getConformanceStatementQuick() throws EFhirClientException { 190 if (conf != null) 191 return conf; 192 return getConformanceStatementQuick(false); 193 } 194 195 public Conformance getConformanceStatementQuick(boolean useOptionsVerb) { 196 Conformance conformance = null; 197 try { 198 if(useOptionsVerb) { 199 conformance = (Conformance)utils.issueOptionsRequest(resourceAddress.getBaseServiceUri(), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference();//TODO fix this 200 } else { 201 conformance = (Conformance)utils.issueGetResourceRequest(resourceAddress.resolveMetadataUri(true), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference(); 202 } 203 } catch(Exception e) { 204 handleException("An error has occurred while trying to fetch the server's conformance statement", e); 205 } 206 return conformance; 207 } 208 209 public <T extends Resource> T read(Class<T> resourceClass, String id) {//TODO Change this to AddressableResource 210 ResourceRequest<T> result = null; 211 try { 212 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), getPreferredResourceFormat(), TIMEOUT_NORMAL); 213 result.addErrorStatus(410);//gone 214 result.addErrorStatus(404);//unknown 215 result.addSuccessStatus(200);//Only one for now 216 if(result.isUnsuccessfulRequest()) { 217 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 218 } 219 } catch (Exception e) { 220 handleException("An error has occurred while trying to read this resource", e); 221 } 222 return result.getPayload(); 223 } 224 225 public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) { 226 ResourceRequest<T> result = null; 227 try { 228 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), getPreferredResourceFormat(), TIMEOUT_NORMAL); 229 result.addErrorStatus(410);//gone 230 result.addErrorStatus(404);//unknown 231 result.addErrorStatus(405);//unknown 232 result.addSuccessStatus(200);//Only one for now 233 if(result.isUnsuccessfulRequest()) { 234 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 235 } 236 } catch (Exception e) { 237 handleException("An error has occurred while trying to read this version of the resource", e); 238 } 239 return result.getPayload(); 240 } 241 242 // GET fhir/ValueSet?url=http://hl7.org/fhir/ValueSet/clinical-findings&version=0.8 243 244 public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) { 245 ResourceRequest<T> result = null; 246 try { 247 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL), getPreferredResourceFormat(), TIMEOUT_NORMAL); 248 result.addErrorStatus(410);//gone 249 result.addErrorStatus(404);//unknown 250 result.addErrorStatus(405);//unknown 251 result.addSuccessStatus(200);//Only one for now 252 if(result.isUnsuccessfulRequest()) { 253 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 254 } 255 } catch (Exception e) { 256 handleException("An error has occurred while trying to read this version of the resource", e); 257 } 258 Bundle bnd = (Bundle) result.getPayload(); 259 if (bnd.getEntry().size() == 0) 260 throw new EFhirClientException("No matching resource found for canonical URL '"+canonicalURL+"'"); 261 if (bnd.getEntry().size() > 1) 262 throw new EFhirClientException("Multiple matching resources found for canonical URL '"+canonicalURL+"'"); 263 return (T) bnd.getEntry().get(0).getResource(); 264 } 265 266 267 public Resource update(Resource resource) { 268 ResourceRequest<Resource> result = null; 269 try { 270 List<Header> headers = null; 271 result = utils.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_OPERATION); 272 result.addErrorStatus(410);//gone 273 result.addErrorStatus(404);//unknown 274 result.addErrorStatus(405); 275 result.addErrorStatus(422);//Unprocessable Entity 276 result.addSuccessStatus(200); 277 result.addSuccessStatus(201); 278 if(result.isUnsuccessfulRequest()) { 279 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 280 } 281 } catch(Exception e) { 282 throw new EFhirClientException("An error has occurred while trying to update this resource", e); 283 } 284 // 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 285 try { 286 OperationOutcome operationOutcome = (OperationOutcome)result.getPayload(); 287 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress.parseCreateLocation(result.getLocation()); 288 return this.vread(resource.getClass(), resVersionedIdentifier.getId(),resVersionedIdentifier.getVersionId()); 289 } catch(ClassCastException e) { 290 // if we fall throught we have the correct type already in the create 291 } 292 293 return result.getPayload(); 294 } 295 296 public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) { 297 ResourceRequest<T> result = null; 298 try { 299 List<Header> headers = null; 300 result = utils.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_OPERATION); 301 result.addErrorStatus(410);//gone 302 result.addErrorStatus(404);//unknown 303 result.addErrorStatus(405); 304 result.addErrorStatus(422);//Unprocessable Entity 305 result.addSuccessStatus(200); 306 result.addSuccessStatus(201); 307 if(result.isUnsuccessfulRequest()) { 308 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 309 } 310 } catch(Exception e) { 311 throw new EFhirClientException("An error has occurred while trying to update this resource", e); 312 } 313 // 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 314 try { 315 OperationOutcome operationOutcome = (OperationOutcome)result.getPayload(); 316 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress.parseCreateLocation(result.getLocation()); 317 return this.vread(resourceClass, resVersionedIdentifier.getId(),resVersionedIdentifier.getVersionId()); 318 } catch(ClassCastException e) { 319 // if we fall throught we have the correct type already in the create 320 } 321 322 return result.getPayload(); 323 } 324 325// 326// public <T extends Resource> boolean delete(Class<T> resourceClass, String id) { 327// try { 328// return utils.issueDeleteRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), proxy); 329// } catch(Exception e) { 330// throw new EFhirClientException("An error has occurred while trying to delete this resource", e); 331// } 332// 333// } 334 335// 336// public <T extends Resource> OperationOutcome create(Class<T> resourceClass, T resource) { 337// ResourceRequest<T> resourceRequest = null; 338// try { 339// List<Header> headers = null; 340// resourceRequest = utils.issuePostRequest(resourceAddress.resolveGetUriFromResourceClass(resourceClass),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, proxy); 341// resourceRequest.addSuccessStatus(201); 342// if(resourceRequest.isUnsuccessfulRequest()) { 343// throw new EFhirClientException("Server responded with HTTP error code " + resourceRequest.getHttpStatus(), (OperationOutcome)resourceRequest.getPayload()); 344// } 345// } catch(Exception e) { 346// handleException("An error has occurred while trying to create this resource", e); 347// } 348// OperationOutcome operationOutcome = null;; 349// try { 350// operationOutcome = (OperationOutcome)resourceRequest.getPayload(); 351// ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = 352// ResourceAddress.parseCreateLocation(resourceRequest.getLocation()); 353// OperationOutcomeIssueComponent issue = operationOutcome.addIssue(); 354// issue.setSeverity(IssueSeverity.INFORMATION); 355// issue.setUserData(ResourceAddress.ResourceVersionedIdentifier.class.toString(), 356// resVersionedIdentifier); 357// return operationOutcome; 358// } catch(ClassCastException e) { 359// // some server (e.g. grahams) returns the resource directly 360// operationOutcome = new OperationOutcome(); 361// OperationOutcomeIssueComponent issue = operationOutcome.addIssue(); 362// issue.setSeverity(IssueSeverity.INFORMATION); 363// issue.setUserData(ResourceRequest.class.toString(), 364// resourceRequest.getPayload()); 365// return operationOutcome; 366// } 367// } 368 369// 370// public <T extends Resource> Bundle history(Calendar 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(Date lastUpdate, Class<T> resourceClass, String id) { 382// Bundle history = null; 383// try { 384// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 385// } catch (Exception e) { 386// handleException("An error has occurred while trying to retrieve history information for this resource", e); 387// } 388// return history; 389// } 390// 391// 392// public <T extends Resource> Bundle history(Calendar 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(Date lastUpdate, Class<T> resourceClass) { 404// Bundle history = null; 405// try { 406// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, lastUpdate, 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) { 415// Bundle history = null; 416// try { 417// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, maxResultSetSize), getPreferredResourceFormat(), proxy); 418// } catch (Exception e) { 419// handleException("An error has occurred while trying to retrieve history information for this resource type", e); 420// } 421// return history; 422// } 423// 424// 425// public <T extends Resource> Bundle history(Class<T> resourceClass, String id) { 426// Bundle history = null; 427// try { 428// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, maxResultSetSize), getPreferredResourceFormat(), proxy); 429// } catch (Exception e) { 430// handleException("An error has occurred while trying to retrieve history information for this resource", e); 431// } 432// return history; 433// } 434// 435// 436// public <T extends Resource> Bundle history(Date 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(Calendar lastUpdate) { 448// Bundle history = null; 449// try { 450// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(lastUpdate, 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 history() { 459// Bundle history = null; 460// try { 461// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(maxResultSetSize), getPreferredResourceFormat(), proxy); 462// } catch (Exception e) { 463// handleException("An error has occurred while trying to retrieve history since last update",e); 464// } 465// return history; 466// } 467// 468// 469// public <T extends Resource> Bundle search(Class<T> resourceClass, Map<String, String> parameters) { 470// Bundle searchResults = null; 471// try { 472// searchResults = utils.issueGetFeedRequest(resourceAddress.resolveSearchUri(resourceClass, parameters), getPreferredResourceFormat(), proxy); 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> Bundle searchPost(Class<T> resourceClass, T resource, Map<String, String> parameters) { 481// Bundle searchResults = null; 482// try { 483// searchResults = utils.issuePostFeedRequest(resourceAddress.resolveSearchUri(resourceClass, new HashMap<String, String>()), parameters, "src", resource, getPreferredResourceFormat()); 484// } catch (Exception e) { 485// handleException("Error performing search with parameters " + parameters, e); 486// } 487// return searchResults; 488// } 489 490 491 public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) { 492 boolean complex = false; 493 for (ParametersParameterComponent p : params.getParameter()) 494 complex = complex || !(p.getValue() instanceof PrimitiveType); 495 Parameters searchResults = null; 496 String ps = ""; 497 try { 498 if (!complex) 499 for (ParametersParameterComponent p : params.getParameter()) 500 if (p.getValue() instanceof PrimitiveType) 501 ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue())+"&"; 502 ResourceRequest<T> result; 503 if (complex) 504 result = utils.issuePostRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG); 505 else 506 result = utils.issueGetResourceRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG); 507 result.addErrorStatus(410);//gone 508 result.addErrorStatus(404);//unknown 509 result.addSuccessStatus(200);//Only one for now 510 if(result.isUnsuccessfulRequest()) 511 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 512 if (result.getPayload() instanceof Parameters) 513 return (Parameters) result.getPayload(); 514 else { 515 Parameters p_out = new Parameters(); 516 p_out.addParameter().setName("return").setResource(result.getPayload()); 517 return p_out; 518 } 519 } catch (Exception e) { 520 handleException("Error performing 2b operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e); 521 } 522 return null; 523 } 524 525 526 public Bundle transaction(Bundle batch) { 527 Bundle transactionResult = null; 528 try { 529 transactionResult = utils.postBatchRequest(resourceAddress.getBaseServiceUri(), utils.getFeedAsByteArray(batch, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), TIMEOUT_NORMAL+batch.getEntry().size()); 530 } catch (Exception e) { 531 handleException("An error occurred trying to process this transaction request", e); 532 } 533 return transactionResult; 534 } 535 536 @SuppressWarnings("unchecked") 537 538 public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) { 539 ResourceRequest<T> result = null; 540 try { 541 result = utils.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id), utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), 3); 542 result.addErrorStatus(400);//gone 543 result.addErrorStatus(422);//Unprocessable Entity 544 result.addSuccessStatus(200);//OK 545 if(result.isUnsuccessfulRequest()) { 546 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 547 } 548 } catch(Exception e) { 549 handleException("An error has occurred while trying to validate this resource", e); 550 } 551 return (OperationOutcome)result.getPayload(); 552 } 553 554 /* change to meta operations 555 556 public List<Coding> getAllTags() { 557 TagListRequest result = null; 558 try { 559 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetAllTags(), getPreferredResourceFormat(), null, proxy); 560 } catch (Exception e) { 561 handleException("An error has occurred while trying to retrieve all tags", e); 562 } 563 return result.getPayload(); 564 } 565 566 567 public <T extends Resource> List<Coding> getAllTagsForResourceType(Class<T> resourceClass) { 568 TagListRequest result = null; 569 try { 570 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetAllTagsForResourceType(resourceClass), getPreferredResourceFormat(), null, proxy); 571 } catch (Exception e) { 572 handleException("An error has occurred while trying to retrieve tags for this resource type", e); 573 } 574 return result.getPayload(); 575 } 576 577 578 public <T extends Resource> List<Coding> getTagsForReference(Class<T> resource, String id) { 579 TagListRequest result = null; 580 try { 581 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetTagsForReference(resource, id), getPreferredResourceFormat(), null, proxy); 582 } catch (Exception e) { 583 handleException("An error has occurred while trying to retrieve tags for this resource", e); 584 } 585 return result.getPayload(); 586 } 587 588 589 public <T extends Resource> List<Coding> getTagsForResourceVersion(Class<T> resource, String id, String versionId) { 590 TagListRequest result = null; 591 try { 592 result = utils.issueGetRequestForTagList(resourceAddress.resolveGetTagsForResourceVersion(resource, id, versionId), getPreferredResourceFormat(), null, proxy); 593 } catch (Exception e) { 594 handleException("An error has occurred while trying to retrieve tags for this resource version", e); 595 } 596 return result.getPayload(); 597 } 598 599// 600// public <T extends Resource> boolean deleteTagsForReference(Class<T> resourceClass, String id) { 601// try { 602// return utils.issueDeleteRequest(resourceAddress.resolveGetTagsForReference(resourceClass, id), 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// 611// public <T extends Resource> boolean deleteTagsForResourceVersion(Class<T> resourceClass, String id, List<Coding> tags, String version) { 612// try { 613// return utils.issueDeleteRequest(resourceAddress.resolveGetTagsForResourceVersion(resourceClass, id, version), proxy); 614// } catch(Exception e) { 615// handleException("An error has occurred while trying to retrieve tags for this resource version", e); 616// throw new EFhirClientException("An error has occurred while trying to delete this resource", e); 617// } 618// } 619 620 621 public <T extends Resource> List<Coding> createTags(List<Coding> tags, Class<T> resourceClass, String id) { 622 TagListRequest request = null; 623 try { 624 request = utils.issuePostRequestForTagList(resourceAddress.resolveGetTagsForReference(resourceClass, id),utils.getTagListAsByteArray(tags, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), null, proxy); 625 request.addSuccessStatus(201); 626 request.addSuccessStatus(200); 627 if(request.isUnsuccessfulRequest()) { 628 throw new EFhirClientException("Server responded with HTTP error code " + request.getHttpStatus()); 629 } 630 } catch(Exception e) { 631 handleException("An error has occurred while trying to set tags for this resource", e); 632 } 633 return request.getPayload(); 634 } 635 636 637 public <T extends Resource> List<Coding> createTags(List<Coding> tags, Class<T> resourceClass, String id, String version) { 638 TagListRequest request = null; 639 try { 640 request = utils.issuePostRequestForTagList(resourceAddress.resolveGetTagsForResourceVersion(resourceClass, id, version),utils.getTagListAsByteArray(tags, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), null, proxy); 641 request.addSuccessStatus(201); 642 request.addSuccessStatus(200); 643 if(request.isUnsuccessfulRequest()) { 644 throw new EFhirClientException("Server responded with HTTP error code " + request.getHttpStatus()); 645 } 646 } catch(Exception e) { 647 handleException("An error has occurred while trying to set the tags for this resource version", e); 648 } 649 return request.getPayload(); 650 } 651 652 653 public <T extends Resource> List<Coding> deleteTags(List<Coding> tags, Class<T> resourceClass, String id, String version) { 654 TagListRequest request = null; 655 try { 656 request = utils.issuePostRequestForTagList(resourceAddress.resolveDeleteTagsForResourceVersion(resourceClass, id, version),utils.getTagListAsByteArray(tags, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), null, proxy); 657 request.addSuccessStatus(201); 658 request.addSuccessStatus(200); 659 if(request.isUnsuccessfulRequest()) { 660 throw new EFhirClientException("Server responded with HTTP error code " + request.getHttpStatus()); 661 } 662 } catch(Exception e) { 663 handleException("An error has occurred while trying to delete the tags for this resource version", e); 664 } 665 return request.getPayload(); 666 } 667 */ 668 669 /** 670 * Helper method to prevent nesting of previously thrown EFhirClientExceptions 671 * 672 * @param e 673 * @throws EFhirClientException 674 */ 675 protected void handleException(String message, Exception e) throws EFhirClientException { 676 if(e instanceof EFhirClientException) { 677 throw (EFhirClientException)e; 678 } else { 679 throw new EFhirClientException(message, e); 680 } 681 } 682 683 /** 684 * Helper method to determine whether desired resource representation 685 * is Json or XML. 686 * 687 * @param format 688 * @return 689 */ 690 protected boolean isJson(String format) { 691 boolean isJson = false; 692 if(format.toLowerCase().contains("json")) { 693 isJson = true; 694 } 695 return isJson; 696 } 697 698 public Bundle fetchFeed(String url) { 699 Bundle feed = null; 700 try { 701 feed = utils.issueGetFeedRequest(new URI(url), getPreferredResourceFormat()); 702 } catch (Exception e) { 703 handleException("An error has occurred while trying to retrieve history since last update",e); 704 } 705 return feed; 706 } 707 708 public ValueSet expandValueset(ValueSet source, Parameters expParams) { 709 List<Header> headers = null; 710 Parameters p = expParams == null ? new Parameters() : expParams.copy(); 711 p.addParameter().setName("valueSet").setResource(source); 712 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"), 713 utils.getResourceAsByteArray(p, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, 4); 714 result.addErrorStatus(410);//gone 715 result.addErrorStatus(404);//unknown 716 result.addErrorStatus(405); 717 result.addErrorStatus(422);//Unprocessable Entity 718 result.addSuccessStatus(200); 719 result.addSuccessStatus(201); 720 if(result.isUnsuccessfulRequest()) { 721 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 722 } 723 return (ValueSet) result.getPayload(); 724 } 725 726 727 public Parameters lookupCode(Map<String, String> params) { 728 ResourceRequest<Resource> result = utils.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params), getPreferredResourceFormat(), TIMEOUT_NORMAL); 729 result.addErrorStatus(410);//gone 730 result.addErrorStatus(404);//unknown 731 result.addErrorStatus(405); 732 result.addErrorStatus(422);//Unprocessable Entity 733 result.addSuccessStatus(200); 734 result.addSuccessStatus(201); 735 if(result.isUnsuccessfulRequest()) { 736 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 737 } 738 return (Parameters) result.getPayload(); 739 } 740 public ValueSet expandValueset(ValueSet source, Parameters expParams,Map<String, String> params) { 741 List<Header> headers = null; 742 Parameters p = expParams == null ? new Parameters() : expParams.copy(); 743 p.addParameter().setName("valueSet").setResource(source); 744 for (String n : params.keySet()) 745 p.addParameter().setName(n).setValue(new StringType(params.get(n))); 746 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", params), 747 utils.getResourceAsByteArray(p, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, 4); 748 result.addErrorStatus(410);//gone 749 result.addErrorStatus(404);//unknown 750 result.addErrorStatus(405); 751 result.addErrorStatus(422);//Unprocessable Entity 752 result.addSuccessStatus(200); 753 result.addSuccessStatus(201); 754 if(result.isUnsuccessfulRequest()) { 755 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 756 } 757 return (ValueSet) result.getPayload(); 758 } 759 760// public ValueSet expandValueset(ValueSet source, ExpansionProfile profile, Map<String, String> params) { 761// List<Header> headers = null; 762// ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", params), 763// utils.getResourceAsByteArray(source, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, proxy); 764// result.addErrorStatus(410);//gone 765// result.addErrorStatus(404);//unknown 766// result.addErrorStatus(405); 767// result.addErrorStatus(422);//Unprocessable Entity 768// result.addSuccessStatus(200); 769// result.addSuccessStatus(201); 770// if(result.isUnsuccessfulRequest()) { 771// throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 772// } 773// return (ValueSet) result.getPayload(); 774// } 775 776 777 public String getAddress() { 778 return base; 779 } 780 781 public ConceptMap initializeClosure(String name) { 782 Parameters params = new Parameters(); 783 params.addParameter().setName("name").setValue(new StringType(name)); 784 List<Header> headers = null; 785 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 786 utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_NORMAL); 787 result.addErrorStatus(410);//gone 788 result.addErrorStatus(404);//unknown 789 result.addErrorStatus(405); 790 result.addErrorStatus(422);//Unprocessable Entity 791 result.addSuccessStatus(200); 792 result.addSuccessStatus(201); 793 if(result.isUnsuccessfulRequest()) { 794 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 795 } 796 return (ConceptMap) result.getPayload(); 797 } 798 799 public ConceptMap updateClosure(String name, Coding coding) { 800 Parameters params = new Parameters(); 801 params.addParameter().setName("name").setValue(new StringType(name)); 802 params.addParameter().setName("concept").setValue(coding); 803 List<Header> headers = null; 804 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 805 utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_OPERATION); 806 result.addErrorStatus(410);//gone 807 result.addErrorStatus(404);//unknown 808 result.addErrorStatus(405); 809 result.addErrorStatus(422);//Unprocessable Entity 810 result.addSuccessStatus(200); 811 result.addSuccessStatus(201); 812 if(result.isUnsuccessfulRequest()) { 813 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 814 } 815 return (ConceptMap) result.getPayload(); 816 } 817 818 public int getTimeout() { 819 return utils.getTimeout(); 820 } 821 822 public void setTimeout(int timeout) { 823 utils.setTimeout(timeout); 824 } 825 826 public String getUsername() { 827 return utils.getUsername(); 828 } 829 830 public void setUsername(String username) { 831 utils.setUsername(username); 832 } 833 834 public String getPassword() { 835 return utils.getPassword(); 836 } 837 838 public void setPassword(String password) { 839 utils.setPassword(password); 840 } 841 842 843 public Parameters getTerminologyCapabilities() { 844 return (Parameters) utils.issueGetResourceRequest(resourceAddress.resolveMetadataTxCaps(), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference(); 845 } 846 847 848 public org.hl7.fhir.utilities.ToolingClientLogger getLogger() { 849 return utils.getLogger(); 850 } 851 852 public void setLogger(ToolingClientLogger logger) { 853 utils.setLogger(logger); 854 } 855 856 public int getRetryCount() { 857 return utils.getRetryCount(); 858 } 859 860 public void setRetryCount(int retryCount) { 861 utils.setRetryCount(retryCount); 862 } 863 864 865}