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