001package org.hl7.fhir.dstu2.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.dstu2.model.Bundle; 045import org.hl7.fhir.dstu2.model.Coding; 046import org.hl7.fhir.dstu2.model.ConceptMap; 047import org.hl7.fhir.dstu2.model.Conformance; 048import org.hl7.fhir.dstu2.model.OperationOutcome; 049import org.hl7.fhir.dstu2.model.Parameters; 050import org.hl7.fhir.dstu2.model.Parameters.ParametersParameterComponent; 051import org.hl7.fhir.dstu2.model.PrimitiveType; 052import org.hl7.fhir.dstu2.model.Resource; 053import org.hl7.fhir.dstu2.model.StringType; 054import org.hl7.fhir.dstu2.model.ValueSet; 055import org.hl7.fhir.utilities.ToolingClientLogger; 056import org.hl7.fhir.utilities.Utilities; 057 058/** 059 * Very Simple RESTful client. This is purely for use in the standalone 060 * tools jar packages. It doesn't support many features, only what the tools 061 * need. 062 * 063 * To use, initialize class and set base service URI as follows: 064 * 065 * <pre><code> 066 * FHIRSimpleClient fhirClient = new FHIRSimpleClient(); 067 * fhirClient.initialize("http://my.fhir.domain/myServiceRoot"); 068 * </code></pre> 069 * 070 * Default Accept and Content-Type headers are application/xml+fhir and application/j+fhir. 071 * 072 * These can be changed by invoking the following setter functions: 073 * 074 * <pre><code> 075 * setPreferredResourceFormat() 076 * setPreferredFeedFormat() 077 * </code></pre> 078 * 079 * TODO Review all sad paths. 080 * 081 * @author Claude Nanjo 082 * 083 */ 084public class FHIRToolingClient { 085 086 public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssK"; 087 public static final String DATE_FORMAT = "yyyy-MM-dd"; 088 public static final String hostKey = "http.proxyHost"; 089 public static final String portKey = "http.proxyPort"; 090 private static final int TIMEOUT_NORMAL = 1; 091 private static final int TIMEOUT_OPERATION = 2; 092 private static final int TIMEOUT_OPERATION_LONG = 3; 093 094 private String base; 095 private ResourceAddress resourceAddress; 096 private ResourceFormat preferredResourceFormat; 097 private HttpHost proxy; 098 private int maxResultSetSize = -1;//_count 099 private Conformance conf; 100 private ClientUtils utils = new ClientUtils(); 101 102 //Pass enpoint for client - URI 103 public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException { 104 preferredResourceFormat = ResourceFormat.RESOURCE_XML; 105 utils.setUserAgent(userAgent); 106 detectProxy(); 107 initialize(baseServiceUrl); 108 } 109 110 public FHIRToolingClient(String baseServiceUrl, String userAgent, String username, String password) throws URISyntaxException { 111 preferredResourceFormat = ResourceFormat.RESOURCE_XML; 112 utils.setUserAgent(userAgent); 113 utils.setUsername(username); 114 utils.setPassword(password); 115 detectProxy(); 116 initialize(baseServiceUrl); 117 } 118 119 public void configureProxy(String proxyHost, int proxyPort) { 120 utils.setProxy(new HttpHost(proxyHost, proxyPort)); 121 } 122 123 public void detectProxy() { 124 String host = System.getenv(hostKey); 125 String port = System.getenv(portKey); 126 127 if(host==null) { 128 host = System.getProperty(hostKey); 129 } 130 131 if(port==null) { 132 port = System.getProperty(portKey); 133 } 134 135 if(host!=null && port!=null) { 136 this.configureProxy(host, Integer.parseInt(port)); 137 } 138 } 139 140 public void initialize(String baseServiceUrl) throws URISyntaxException { 141 base = baseServiceUrl; 142 resourceAddress = new ResourceAddress(baseServiceUrl); 143 this.maxResultSetSize = -1; 144 checkConformance(); 145 } 146 147 private void checkConformance() { 148 try { 149 conf = getConformanceStatementQuick(); 150 } catch (Throwable e) { 151 } 152 } 153 154 public String getPreferredResourceFormat() { 155 return preferredResourceFormat.getHeader(); 156 } 157 158 public void setPreferredResourceFormat(ResourceFormat resourceFormat) { 159 preferredResourceFormat = resourceFormat; 160 } 161 162 public int getMaximumRecordCount() { 163 return maxResultSetSize; 164 } 165 166 public void setMaximumRecordCount(int maxResultSetSize) { 167 this.maxResultSetSize = maxResultSetSize; 168 } 169 170 public Conformance getConformanceStatement() throws EFhirClientException { 171 if (conf != null) 172 return conf; 173 return getConformanceStatement(false); 174 } 175 176 public Conformance getConformanceStatement(boolean useOptionsVerb) { 177 Conformance conformance = null; 178 try { 179 if(useOptionsVerb) { 180 conformance = (Conformance)utils.issueOptionsRequest(resourceAddress.getBaseServiceUri(), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference();//TODO fix this 181 } else { 182 conformance = (Conformance)utils.issueGetResourceRequest(resourceAddress.resolveMetadataUri(false), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference(); 183 } 184 } catch(Exception e) { 185 handleException("An error has occurred while trying to fetch the server's conformance statement", e); 186 } 187 return conformance; 188 } 189 190 public Conformance getConformanceStatementQuick() throws EFhirClientException { 191 if (conf != null) 192 return conf; 193 return getConformanceStatementQuick(false); 194 } 195 196 public Conformance getConformanceStatementQuick(boolean useOptionsVerb) { 197 Conformance conformance = null; 198 try { 199 if(useOptionsVerb) { 200 conformance = (Conformance)utils.issueOptionsRequest(resourceAddress.getBaseServiceUri(), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference();//TODO fix this 201 } else { 202 conformance = (Conformance)utils.issueGetResourceRequest(resourceAddress.resolveMetadataUri(true), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference(); 203 } 204 } catch(Exception e) { 205 handleException("An error has occurred while trying to fetch the server's conformance statement", e); 206 } 207 return conformance; 208 } 209 210 public <T extends Resource> T read(Class<T> resourceClass, String id) {//TODO Change this to AddressableResource 211 ResourceRequest<T> result = null; 212 try { 213 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), getPreferredResourceFormat(), TIMEOUT_NORMAL); 214 result.addErrorStatus(410);//gone 215 result.addErrorStatus(404);//unknown 216 result.addSuccessStatus(200);//Only one for now 217 if(result.isUnsuccessfulRequest()) { 218 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 219 } 220 } catch (Exception e) { 221 handleException("An error has occurred while trying to read this resource", e); 222 } 223 return result.getPayload(); 224 } 225 226 public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) { 227 ResourceRequest<T> result = null; 228 try { 229 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), getPreferredResourceFormat(), TIMEOUT_NORMAL); 230 result.addErrorStatus(410);//gone 231 result.addErrorStatus(404);//unknown 232 result.addErrorStatus(405);//unknown 233 result.addSuccessStatus(200);//Only one for now 234 if(result.isUnsuccessfulRequest()) { 235 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 236 } 237 } catch (Exception e) { 238 handleException("An error has occurred while trying to read this version of the resource", e); 239 } 240 return result.getPayload(); 241 } 242 243 // GET fhir/ValueSet?url=http://hl7.org/fhir/ValueSet/clinical-findings&version=0.8 244 245 public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) { 246 ResourceRequest<T> result = null; 247 try { 248 result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL), getPreferredResourceFormat(), TIMEOUT_NORMAL); 249 result.addErrorStatus(410);//gone 250 result.addErrorStatus(404);//unknown 251 result.addErrorStatus(405);//unknown 252 result.addSuccessStatus(200);//Only one for now 253 if(result.isUnsuccessfulRequest()) { 254 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 255 } 256 } catch (Exception e) { 257 handleException("An error has occurred while trying to read this version of the resource", e); 258 } 259 Bundle bnd = (Bundle) result.getPayload(); 260 if (bnd.getEntry().size() == 0) 261 throw new EFhirClientException("No matching resource found for canonical URL '"+canonicalURL+"'"); 262 if (bnd.getEntry().size() > 1) 263 throw new EFhirClientException("Multiple matching resources found for canonical URL '"+canonicalURL+"'"); 264 return (T) bnd.getEntry().get(0).getResource(); 265 } 266 267 268 public Resource update(Resource resource) { 269 ResourceRequest<Resource> result = null; 270 try { 271 List<Header> headers = null; 272 result = utils.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_OPERATION); 273 result.addErrorStatus(410);//gone 274 result.addErrorStatus(404);//unknown 275 result.addErrorStatus(405); 276 result.addErrorStatus(422);//Unprocessable Entity 277 result.addSuccessStatus(200); 278 result.addSuccessStatus(201); 279 if(result.isUnsuccessfulRequest()) { 280 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 281 } 282 } catch(Exception e) { 283 throw new EFhirClientException("An error has occurred while trying to update this resource", e); 284 } 285 // 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 286 try { 287 OperationOutcome operationOutcome = (OperationOutcome)result.getPayload(); 288 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress.parseCreateLocation(result.getLocation()); 289 return this.vread(resource.getClass(), resVersionedIdentifier.getId(),resVersionedIdentifier.getVersionId()); 290 } catch(ClassCastException e) { 291 // if we fall throught we have the correct type already in the create 292 } 293 294 return result.getPayload(); 295 } 296 297 public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) { 298 ResourceRequest<T> result = null; 299 try { 300 List<Header> headers = null; 301 result = utils.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_OPERATION); 302 result.addErrorStatus(410);//gone 303 result.addErrorStatus(404);//unknown 304 result.addErrorStatus(405); 305 result.addErrorStatus(422);//Unprocessable Entity 306 result.addSuccessStatus(200); 307 result.addSuccessStatus(201); 308 if(result.isUnsuccessfulRequest()) { 309 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 310 } 311 } catch(Exception e) { 312 throw new EFhirClientException("An error has occurred while trying to update this resource", e); 313 } 314 // 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 315 try { 316 OperationOutcome operationOutcome = (OperationOutcome)result.getPayload(); 317 ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress.parseCreateLocation(result.getLocation()); 318 return this.vread(resourceClass, resVersionedIdentifier.getId(),resVersionedIdentifier.getVersionId()); 319 } catch(ClassCastException e) { 320 // if we fall throught we have the correct type already in the create 321 } 322 323 return result.getPayload(); 324 } 325 326// 327// public <T extends Resource> boolean delete(Class<T> resourceClass, String id) { 328// try { 329// return utils.issueDeleteRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), proxy); 330// } catch(Exception e) { 331// throw new EFhirClientException("An error has occurred while trying to delete this resource", e); 332// } 333// 334// } 335 336// 337// public <T extends Resource> OperationOutcome create(Class<T> resourceClass, T resource) { 338// ResourceRequest<T> resourceRequest = null; 339// try { 340// List<Header> headers = null; 341// resourceRequest = utils.issuePostRequest(resourceAddress.resolveGetUriFromResourceClass(resourceClass),utils.getResourceAsByteArray(resource, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, proxy); 342// resourceRequest.addSuccessStatus(201); 343// if(resourceRequest.isUnsuccessfulRequest()) { 344// throw new EFhirClientException("Server responded with HTTP error code " + resourceRequest.getHttpStatus(), (OperationOutcome)resourceRequest.getPayload()); 345// } 346// } catch(Exception e) { 347// handleException("An error has occurred while trying to create this resource", e); 348// } 349// OperationOutcome operationOutcome = null;; 350// try { 351// operationOutcome = (OperationOutcome)resourceRequest.getPayload(); 352// ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = 353// ResourceAddress.parseCreateLocation(resourceRequest.getLocation()); 354// OperationOutcomeIssueComponent issue = operationOutcome.addIssue(); 355// issue.setSeverity(IssueSeverity.INFORMATION); 356// issue.setUserData(ResourceAddress.ResourceVersionedIdentifier.class.toString(), 357// resVersionedIdentifier); 358// return operationOutcome; 359// } catch(ClassCastException e) { 360// // some server (e.g. grahams) returns the resource directly 361// operationOutcome = new OperationOutcome(); 362// OperationOutcomeIssueComponent issue = operationOutcome.addIssue(); 363// issue.setSeverity(IssueSeverity.INFORMATION); 364// issue.setUserData(ResourceRequest.class.toString(), 365// resourceRequest.getPayload()); 366// return operationOutcome; 367// } 368// } 369 370// 371// public <T extends Resource> Bundle history(Calendar lastUpdate, Class<T> resourceClass, String id) { 372// Bundle history = null; 373// try { 374// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 375// } catch (Exception e) { 376// handleException("An error has occurred while trying to retrieve history information for this resource", e); 377// } 378// return history; 379// } 380 381// 382// public <T extends Resource> Bundle history(Date lastUpdate, Class<T> resourceClass, String id) { 383// Bundle history = null; 384// try { 385// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 386// } catch (Exception e) { 387// handleException("An error has occurred while trying to retrieve history information for this resource", e); 388// } 389// return history; 390// } 391// 392// 393// public <T extends Resource> Bundle history(Calendar lastUpdate, Class<T> resourceClass) { 394// Bundle history = null; 395// try { 396// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 397// } catch (Exception e) { 398// handleException("An error has occurred while trying to retrieve history information for this resource type", e); 399// } 400// return history; 401// } 402// 403// 404// public <T extends Resource> Bundle history(Date lastUpdate, Class<T> resourceClass) { 405// Bundle history = null; 406// try { 407// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 408// } catch (Exception e) { 409// handleException("An error has occurred while trying to retrieve history information for this resource type", e); 410// } 411// return history; 412// } 413// 414// 415// public <T extends Resource> Bundle history(Class<T> resourceClass) { 416// Bundle history = null; 417// try { 418// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceType(resourceClass, maxResultSetSize), getPreferredResourceFormat(), proxy); 419// } catch (Exception e) { 420// handleException("An error has occurred while trying to retrieve history information for this resource type", e); 421// } 422// return history; 423// } 424// 425// 426// public <T extends Resource> Bundle history(Class<T> resourceClass, String id) { 427// Bundle history = null; 428// try { 429// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForResourceId(resourceClass, id, maxResultSetSize), getPreferredResourceFormat(), proxy); 430// } catch (Exception e) { 431// handleException("An error has occurred while trying to retrieve history information for this resource", e); 432// } 433// return history; 434// } 435// 436// 437// public <T extends Resource> Bundle history(Date lastUpdate) { 438// Bundle history = null; 439// try { 440// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 441// } catch (Exception e) { 442// handleException("An error has occurred while trying to retrieve history since last update",e); 443// } 444// return history; 445// } 446// 447// 448// public <T extends Resource> Bundle history(Calendar lastUpdate) { 449// Bundle history = null; 450// try { 451// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(lastUpdate, maxResultSetSize), getPreferredResourceFormat(), proxy); 452// } catch (Exception e) { 453// handleException("An error has occurred while trying to retrieve history since last update",e); 454// } 455// return history; 456// } 457// 458// 459// public <T extends Resource> Bundle history() { 460// Bundle history = null; 461// try { 462// history = utils.issueGetFeedRequest(resourceAddress.resolveGetHistoryForAllResources(maxResultSetSize), getPreferredResourceFormat(), proxy); 463// } catch (Exception e) { 464// handleException("An error has occurred while trying to retrieve history since last update",e); 465// } 466// return history; 467// } 468// 469// 470// public <T extends Resource> Bundle search(Class<T> resourceClass, Map<String, String> parameters) { 471// Bundle searchResults = null; 472// try { 473// searchResults = utils.issueGetFeedRequest(resourceAddress.resolveSearchUri(resourceClass, parameters), getPreferredResourceFormat(), proxy); 474// } catch (Exception e) { 475// handleException("Error performing search with parameters " + parameters, e); 476// } 477// return searchResults; 478// } 479// 480// 481// public <T extends Resource> Bundle searchPost(Class<T> resourceClass, T resource, Map<String, String> parameters) { 482// Bundle searchResults = null; 483// try { 484// searchResults = utils.issuePostFeedRequest(resourceAddress.resolveSearchUri(resourceClass, new HashMap<String, String>()), parameters, "src", resource, getPreferredResourceFormat()); 485// } catch (Exception e) { 486// handleException("Error performing search with parameters " + parameters, e); 487// } 488// return searchResults; 489// } 490 491 492 public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) { 493 boolean complex = false; 494 for (ParametersParameterComponent p : params.getParameter()) 495 complex = complex || !(p.getValue() instanceof PrimitiveType); 496 Parameters searchResults = null; 497 String ps = ""; 498 try { 499 if (!complex) 500 for (ParametersParameterComponent p : params.getParameter()) 501 if (p.getValue() instanceof PrimitiveType) 502 ps += p.getName() + "=" + Utilities.encodeUri(((PrimitiveType) p.getValue()).asStringValue())+"&"; 503 ResourceRequest<T> result; 504 if (complex) 505 result = utils.issuePostRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG); 506 else 507 result = utils.issueGetResourceRequest(resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps), getPreferredResourceFormat(), TIMEOUT_OPERATION_LONG); 508 result.addErrorStatus(410);//gone 509 result.addErrorStatus(404);//unknown 510 result.addSuccessStatus(200);//Only one for now 511 if(result.isUnsuccessfulRequest()) 512 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 513 if (result.getPayload() instanceof Parameters) 514 return (Parameters) result.getPayload(); 515 else { 516 Parameters p_out = new Parameters(); 517 p_out.addParameter().setName("return").setResource(result.getPayload()); 518 return p_out; 519 } 520 } catch (Exception e) { 521 handleException("Error performing tx2 operation '"+name+": "+e.getMessage()+"' (parameters = \"" + ps+"\")", e); 522 } 523 return null; 524 } 525 526 527 public Bundle transaction(Bundle batch) { 528 Bundle transactionResult = null; 529 try { 530 transactionResult = utils.postBatchRequest(resourceAddress.getBaseServiceUri(), utils.getFeedAsByteArray(batch, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), TIMEOUT_NORMAL+batch.getEntry().size()); 531 } catch (Exception e) { 532 handleException("An error occurred trying to process this transaction request", e); 533 } 534 return transactionResult; 535 } 536 537 @SuppressWarnings("unchecked") 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) { 709 List<Header> headers = null; 710 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"), 711 utils.getResourceAsByteArray(source, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_OPERATION_LONG); 712 result.addErrorStatus(410);//gone 713 result.addErrorStatus(404);//unknown 714 result.addErrorStatus(405); 715 result.addErrorStatus(422);//Unprocessable Entity 716 result.addSuccessStatus(200); 717 result.addSuccessStatus(201); 718 if(result.isUnsuccessfulRequest()) { 719 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 720 } 721 return (ValueSet) result.getPayload(); 722 } 723 724 725 public Parameters lookupCode(Map<String, String> params) { 726 ResourceRequest<Resource> result = utils.issueGetResourceRequest(resourceAddress.resolveOperationUri(ValueSet.class, "lookup", params), getPreferredResourceFormat(), TIMEOUT_NORMAL); 727 result.addErrorStatus(410);//gone 728 result.addErrorStatus(404);//unknown 729 result.addErrorStatus(405); 730 result.addErrorStatus(422);//Unprocessable Entity 731 result.addSuccessStatus(200); 732 result.addSuccessStatus(201); 733 if(result.isUnsuccessfulRequest()) { 734 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 735 } 736 return (Parameters) result.getPayload(); 737 } 738 public ValueSet expandValueset(ValueSet source, Parameters expParams,Map<String, String> params) { 739 List<Header> headers = null; 740 Parameters p = expParams == null ? new Parameters() : expParams.copy(); 741 p.addParameter().setName("valueSet").setResource(source); 742 for (String n : params.keySet()) 743 p.addParameter().setName(n).setValue(new StringType(params.get(n))); 744 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", params), 745 utils.getResourceAsByteArray(p, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, 4); 746 result.addErrorStatus(410); //gone 747 result.addErrorStatus(404); //unknown 748 result.addErrorStatus(405); 749 result.addErrorStatus(422); //Unprocessable Entity 750 result.addSuccessStatus(200); 751 result.addSuccessStatus(201); 752 if(result.isUnsuccessfulRequest()) { 753 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 754 } 755 return (ValueSet) result.getPayload(); 756 } 757 758// public ValueSet expandValueset(ValueSet source, ExpansionProfile profile, Map<String, String> params) { 759// List<Header> headers = null; 760// ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", params), 761// utils.getResourceAsByteArray(source, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, proxy); 762// result.addErrorStatus(410);//gone 763// result.addErrorStatus(404);//unknown 764// result.addErrorStatus(405); 765// result.addErrorStatus(422);//Unprocessable Entity 766// result.addSuccessStatus(200); 767// result.addSuccessStatus(201); 768// if(result.isUnsuccessfulRequest()) { 769// throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 770// } 771// return (ValueSet) result.getPayload(); 772// } 773 774 775 public String getAddress() { 776 return base; 777 } 778 779 public ConceptMap initializeClosure(String name) { 780 Parameters params = new Parameters(); 781 params.addParameter().setName("name").setValue(new StringType(name)); 782 List<Header> headers = null; 783 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 784 utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_NORMAL); 785 result.addErrorStatus(410);//gone 786 result.addErrorStatus(404);//unknown 787 result.addErrorStatus(405); 788 result.addErrorStatus(422);//Unprocessable Entity 789 result.addSuccessStatus(200); 790 result.addSuccessStatus(201); 791 if(result.isUnsuccessfulRequest()) { 792 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 793 } 794 return (ConceptMap) result.getPayload(); 795 } 796 797 public ConceptMap updateClosure(String name, Coding coding) { 798 Parameters params = new Parameters(); 799 params.addParameter().setName("name").setValue(new StringType(name)); 800 params.addParameter().setName("concept").setValue(coding); 801 List<Header> headers = null; 802 ResourceRequest<Resource> result = utils.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()), 803 utils.getResourceAsByteArray(params, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), headers, TIMEOUT_OPERATION); 804 result.addErrorStatus(410);//gone 805 result.addErrorStatus(404);//unknown 806 result.addErrorStatus(405); 807 result.addErrorStatus(422);//Unprocessable Entity 808 result.addSuccessStatus(200); 809 result.addSuccessStatus(201); 810 if(result.isUnsuccessfulRequest()) { 811 throw new EFhirClientException("Server returned error code " + result.getHttpStatus(), (OperationOutcome)result.getPayload()); 812 } 813 return (ConceptMap) result.getPayload(); 814 } 815 816 public int getTimeout() { 817 return utils.getTimeout(); 818 } 819 820 public void setTimeout(int timeout) { 821 utils.setTimeout(timeout); 822 } 823 824 public String getUsername() { 825 return utils.getUsername(); 826 } 827 828 public void setUsername(String username) { 829 utils.setUsername(username); 830 } 831 832 public String getPassword() { 833 return utils.getPassword(); 834 } 835 836 public void setPassword(String password) { 837 utils.setPassword(password); 838 } 839 840 841 public Parameters getTerminologyCapabilities() { 842 return (Parameters) utils.issueGetResourceRequest(resourceAddress.resolveMetadataTxCaps(), getPreferredResourceFormat(), TIMEOUT_NORMAL).getReference(); 843 } 844 845 846 public org.hl7.fhir.utilities.ToolingClientLogger getLogger() { 847 return utils.getLogger(); 848 } 849 850 public void setLogger(ToolingClientLogger logger) { 851 utils.setLogger(logger); 852 } 853 854 public int getRetryCount() { 855 return utils.getRetryCount(); 856 } 857 858 public void setRetryCount(int retryCount) { 859 utils.setRetryCount(retryCount); 860 } 861 862 public String getUserAgent() { 863 return utils.getUserAgent(); 864 } 865 866 public void setUserAgent(String userAgent) { 867 utils.setUserAgent(userAgent); 868 } 869 870 public String getServerVersion() { 871 return conf == null ? null : conf.getSoftware().getVersion(); 872 } 873}