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