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