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