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