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