001package ca.uhn.fhir.rest.client.method;
002
003import ca.uhn.fhir.context.ConfigurationException;
004import ca.uhn.fhir.context.FhirContext;
005import ca.uhn.fhir.context.FhirVersionEnum;
006import ca.uhn.fhir.context.RuntimeResourceDefinition;
007import ca.uhn.fhir.i18n.Msg;
008import ca.uhn.fhir.model.api.IResource;
009import ca.uhn.fhir.model.api.Include;
010import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
011import ca.uhn.fhir.model.api.TagList;
012import ca.uhn.fhir.model.primitive.IdDt;
013import ca.uhn.fhir.model.primitive.InstantDt;
014import ca.uhn.fhir.parser.IParser;
015import ca.uhn.fhir.rest.annotation.At;
016import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
017import ca.uhn.fhir.rest.annotation.Count;
018import ca.uhn.fhir.rest.annotation.Elements;
019import ca.uhn.fhir.rest.annotation.IdParam;
020import ca.uhn.fhir.rest.annotation.IncludeParam;
021import ca.uhn.fhir.rest.annotation.Offset;
022import ca.uhn.fhir.rest.annotation.Operation;
023import ca.uhn.fhir.rest.annotation.OperationParam;
024import ca.uhn.fhir.rest.annotation.OptionalParam;
025import ca.uhn.fhir.rest.annotation.RawParam;
026import ca.uhn.fhir.rest.annotation.RequiredParam;
027import ca.uhn.fhir.rest.annotation.ResourceParam;
028import ca.uhn.fhir.rest.annotation.Since;
029import ca.uhn.fhir.rest.annotation.Sort;
030import ca.uhn.fhir.rest.annotation.TransactionParam;
031import ca.uhn.fhir.rest.annotation.Validate;
032import ca.uhn.fhir.rest.api.Constants;
033import ca.uhn.fhir.rest.api.EncodingEnum;
034import ca.uhn.fhir.rest.api.MethodOutcome;
035import ca.uhn.fhir.rest.api.PatchTypeEnum;
036import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
037import ca.uhn.fhir.rest.api.SummaryEnum;
038import ca.uhn.fhir.rest.api.ValidationModeEnum;
039import ca.uhn.fhir.rest.client.api.IHttpRequest;
040import ca.uhn.fhir.rest.client.method.OperationParameter.IOperationParamConverter;
041import ca.uhn.fhir.rest.param.ParameterUtil;
042import ca.uhn.fhir.rest.param.binder.CollectionBinder;
043import ca.uhn.fhir.util.DateUtils;
044import ca.uhn.fhir.util.ParametersUtil;
045import ca.uhn.fhir.util.ReflectionUtil;
046import ca.uhn.fhir.util.UrlUtil;
047import org.apache.commons.lang3.StringUtils;
048import org.hl7.fhir.instance.model.api.IAnyResource;
049import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
050import org.hl7.fhir.instance.model.api.IBaseResource;
051import org.hl7.fhir.instance.model.api.IIdType;
052
053import java.io.IOException;
054import java.io.InputStream;
055import java.io.PushbackInputStream;
056import java.lang.annotation.Annotation;
057import java.lang.reflect.Method;
058import java.util.ArrayList;
059import java.util.Collection;
060import java.util.Date;
061import java.util.List;
062import java.util.Map;
063import java.util.Map.Entry;
064
065import static org.apache.commons.lang3.StringUtils.isNotBlank;
066
067/*
068 * #%L
069 * HAPI FHIR - Client Framework
070 * %%
071 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
072 * %%
073 * Licensed under the Apache License, Version 2.0 (the "License");
074 * you may not use this file except in compliance with the License.
075 * You may obtain a copy of the License at
076 *
077 * http://www.apache.org/licenses/LICENSE-2.0
078 *
079 * Unless required by applicable law or agreed to in writing, software
080 * distributed under the License is distributed on an "AS IS" BASIS,
081 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
082 * See the License for the specific language governing permissions and
083 * limitations under the License.
084 * #L%
085 */
086
087public class MethodUtil {
088
089        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class);
090
091        /** Non instantiable */
092        private MethodUtil() {
093                // nothing
094        }
095
096        public static void addAcceptHeaderToRequest(EncodingEnum theEncoding, IHttpRequest theHttpRequest,
097                        FhirContext theContext) {
098                if (theEncoding == null) {
099                        if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2_1) == false) {
100                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY);
101                        } else {
102                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY);
103                        }
104                } else if (theEncoding == EncodingEnum.JSON) {
105                        if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2_1) == false) {
106                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
107                        } else {
108                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY);
109                        }
110                } else if (theEncoding == EncodingEnum.XML) {
111                        if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2_1) == false) {
112                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
113                        } else {
114                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY);
115                        }
116                }
117
118        }
119
120        public static HttpGetClientInvocation createConformanceInvocation(FhirContext theContext) {
121                return new HttpGetClientInvocation(theContext, "metadata");
122        }
123
124        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, FhirContext theContext) {
125                return createCreateInvocation(theResource, null, theContext);
126        }
127
128        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, String theResourceBody,
129                        FhirContext theContext) {
130                RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
131                String resourceName = def.getName();
132
133                StringBuilder urlExtension = new StringBuilder();
134                urlExtension.append(resourceName);
135
136                HttpPostClientInvocation retVal;
137                if (StringUtils.isBlank(theResourceBody)) {
138                        retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString());
139                } else {
140                        retVal = new HttpPostClientInvocation(theContext, theResourceBody, false, urlExtension.toString());
141                }
142
143                retVal.setOmitResourceId(true);
144
145                return retVal;
146        }
147
148        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, String theResourceBody,
149                        FhirContext theContext, Map<String, List<String>> theIfNoneExistParams) {
150                HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theContext);
151                retVal.setIfNoneExistParams(theIfNoneExistParams);
152                return retVal;
153        }
154
155        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, String theResourceBody,
156                        FhirContext theContext, String theIfNoneExistUrl) {
157                HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theContext);
158                retVal.setIfNoneExistString(theIfNoneExistUrl);
159                return retVal;
160        }
161
162        public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, IIdType theId,
163                        PatchTypeEnum thePatchType, String theBody) {
164                return PatchMethodBinding.createPatchInvocation(theContext, theId, thePatchType, theBody);
165        }
166
167        public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, PatchTypeEnum thePatchType,
168                        String theBody, String theResourceType, Map<String, List<String>> theMatchParams) {
169                return PatchMethodBinding.createPatchInvocation(theContext, thePatchType, theBody, theResourceType,
170                                theMatchParams);
171        }
172
173        public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, String theUrl,
174                        PatchTypeEnum thePatchType, String theBody) {
175                return PatchMethodBinding.createPatchInvocation(theContext, theUrl, thePatchType, theBody);
176        }
177
178        public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource,
179                        String theResourceBody, Map<String, List<String>> theMatchParams) {
180                String resourceType = theContext.getResourceType(theResource);
181
182                StringBuilder b = createUrl(resourceType, theMatchParams);
183
184                HttpPutClientInvocation retVal;
185                if (StringUtils.isBlank(theResourceBody)) {
186                        retVal = new HttpPutClientInvocation(theContext, theResource, b.toString());
187                } else {
188                        retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, b.toString());
189                }
190
191                return retVal;
192        }
193
194        public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource,
195                        String theResourceBody, String theMatchUrl) {
196                HttpPutClientInvocation retVal;
197                if (StringUtils.isBlank(theResourceBody)) {
198                        retVal = new HttpPutClientInvocation(theContext, theResource, theMatchUrl);
199                } else {
200                        retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, theMatchUrl);
201                }
202
203                return retVal;
204        }
205
206        public static HttpPutClientInvocation createUpdateInvocation(IBaseResource theResource, String theResourceBody,
207                                                                                                                                                                         IIdType theId, FhirContext theContext) {
208                String resourceName = theContext.getResourceType(theResource);
209                StringBuilder urlBuilder = new StringBuilder();
210                urlBuilder.append(resourceName);
211                urlBuilder.append('/');
212                urlBuilder.append(theId.getIdPart());
213                String urlExtension = urlBuilder.toString();
214
215                HttpPutClientInvocation retVal;
216                if (StringUtils.isBlank(theResourceBody)) {
217                        retVal = new HttpPutClientInvocation(theContext, theResource, urlExtension);
218                } else {
219                        retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, urlExtension);
220                }
221
222                retVal.setForceResourceId(theId);
223
224                if (theId.hasVersionIdPart()) {
225                        retVal.addHeader(Constants.HEADER_IF_MATCH, '"' + theId.getVersionIdPart() + '"');
226                }
227
228                return retVal;
229        }
230
231        public static HttpPutClientInvocation createUpdateHistoryRewriteInvocation(IBaseResource theResource, String theResourceBody,
232                                                                                                                                                                                                                IIdType theId, FhirContext theContext) {
233                String resourceName = theContext.getResourceType(theResource);
234                StringBuilder urlBuilder = new StringBuilder();
235                urlBuilder.append(resourceName);
236                urlBuilder.append('/');
237                urlBuilder.append(theId.getIdPart());
238                if (theId.hasVersionIdPart()) {
239                        urlBuilder.append('/');
240                        urlBuilder.append(Constants.PARAM_HISTORY);
241                        urlBuilder.append('/');
242                        urlBuilder.append(theId.getVersionIdPart());
243                }
244
245                String urlExtension = urlBuilder.toString();
246
247                HttpPutClientInvocation retVal;
248                if (StringUtils.isBlank(theResourceBody)) {
249                        retVal = new HttpPutClientInvocation(theContext, theResource, urlExtension);
250                } else {
251                        retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, urlExtension);
252                }
253
254                return retVal;
255        }
256
257        public static StringBuilder createUrl(String theResourceType, Map<String, List<String>> theMatchParams) {
258                StringBuilder b = new StringBuilder();
259
260                b.append(theResourceType);
261
262                boolean haveQuestionMark = false;
263                for (Entry<String, List<String>> nextEntry : theMatchParams.entrySet()) {
264                        for (String nextValue : nextEntry.getValue()) {
265                                b.append(haveQuestionMark ? '&' : '?');
266                                haveQuestionMark = true;
267                                b.append(UrlUtil.escapeUrlParam(nextEntry.getKey()));
268                                b.append('=');
269                                b.append(UrlUtil.escapeUrlParam(nextValue));
270                        }
271                }
272                return b;
273        }
274
275        @SuppressWarnings("unchecked")
276        public static List<IParameter> getResourceParameters(final FhirContext theContext, Method theMethod,
277                        Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) {
278                List<IParameter> parameters = new ArrayList<>();
279
280                Class<?>[] parameterTypes = theMethod.getParameterTypes();
281                int paramIndex = 0;
282                for (Annotation[] annotations : theMethod.getParameterAnnotations()) {
283
284                        IParameter param = null;
285                        Class<?> parameterType = parameterTypes[paramIndex];
286                        Class<? extends java.util.Collection<?>> outerCollectionType = null;
287                        Class<? extends java.util.Collection<?>> innerCollectionType = null;
288                        if (TagList.class.isAssignableFrom(parameterType)) {
289                                // TagList is handled directly within the method bindings
290                                param = new NullParameter();
291                        } else {
292                                if (Collection.class.isAssignableFrom(parameterType)) {
293                                        innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
294                                        parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
295                                }
296                                if (Collection.class.isAssignableFrom(parameterType)) {
297                                        outerCollectionType = innerCollectionType;
298                                        innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
299                                        parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
300                                }
301                                if (Collection.class.isAssignableFrom(parameterType)) {
302                                        throw new ConfigurationException(Msg.code(1433) + "Argument #" + paramIndex + " of Method '" + theMethod.getName()
303                                                        + "' in type '" + theMethod.getDeclaringClass().getCanonicalName()
304                                                        + "' is of an invalid generic type (can not be a collection of a collection of a collection)");
305                                }
306                        }
307
308                        if (parameterType.equals(SummaryEnum.class)) {
309                                param = new SummaryEnumParameter();
310                        } else if (parameterType.equals(PatchTypeEnum.class)) {
311                                param = new PatchTypeParameter();
312                        } else {
313                                for (int i = 0; i < annotations.length && param == null; i++) {
314                                        Annotation nextAnnotation = annotations[i];
315
316                                        if (nextAnnotation instanceof RequiredParam) {
317                                                SearchParameter parameter = new SearchParameter();
318                                                parameter.setName(((RequiredParam) nextAnnotation).name());
319                                                parameter.setRequired(true);
320                                                parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes());
321                                                parameter.setCompositeTypes(((RequiredParam) nextAnnotation).compositeTypes());
322                                                parameter.setChainlists(((RequiredParam) nextAnnotation).chainWhitelist());
323                                                parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
324                                                param = parameter;
325                                        } else if (nextAnnotation instanceof OptionalParam) {
326                                                SearchParameter parameter = new SearchParameter();
327                                                parameter.setName(((OptionalParam) nextAnnotation).name());
328                                                parameter.setRequired(false);
329                                                parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes());
330                                                parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes());
331                                                parameter.setChainlists(((OptionalParam) nextAnnotation).chainWhitelist());
332                                                parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
333                                                param = parameter;
334                                        } else if (nextAnnotation instanceof RawParam) {
335                                                param = new RawParamsParmeter();
336                                        } else if (nextAnnotation instanceof IncludeParam) {
337                                                Class<? extends Collection<Include>> instantiableCollectionType;
338                                                Class<?> specType;
339
340                                                if (parameterType == String.class) {
341                                                        instantiableCollectionType = null;
342                                                        specType = String.class;
343                                                } else if ((parameterType != Include.class) || innerCollectionType == null
344                                                                || outerCollectionType != null) {
345                                                        throw new ConfigurationException(Msg.code(1434) + "Method '" + theMethod.getName() + "' is annotated with @"
346                                                                        + IncludeParam.class.getSimpleName() + " but has a type other than Collection<"
347                                                                        + Include.class.getSimpleName() + ">");
348                                                } else {
349                                                        instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder
350                                                                        .getInstantiableCollectionType(innerCollectionType,
351                                                                                        "Method '" + theMethod.getName() + "'");
352                                                        specType = parameterType;
353                                                }
354
355                                                param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType,                                                         specType);
356                                        } else if (nextAnnotation instanceof ResourceParam) {
357                                                if (IBaseResource.class.isAssignableFrom(parameterType)) {
358                                                        // good
359                                                } else if (String.class.equals(parameterType)) {
360                                                        // good
361                                                } else {
362                                                        StringBuilder b = new StringBuilder();
363                                                        b.append("Method '");
364                                                        b.append(theMethod.getName());
365                                                        b.append("' is annotated with @");
366                                                        b.append(ResourceParam.class.getSimpleName());
367                                                        b.append(" but has a type that is not an implementation of ");
368                                                        b.append(IBaseResource.class.getCanonicalName());
369                                                        throw new ConfigurationException(Msg.code(1435) + b.toString());
370                                                }
371                                                param = new ResourceParameter(parameterType);
372                                        } else if (nextAnnotation instanceof IdParam) {
373                                                param = new NullParameter();
374                                        } else if (nextAnnotation instanceof Elements) {
375                                                param = new ElementsParameter();
376                                        } else if (nextAnnotation instanceof Since) {
377                                                param = new SinceParameter();
378                                                ((SinceParameter) param).setType(theContext, parameterType, innerCollectionType,
379                                                                outerCollectionType);
380                                        } else if (nextAnnotation instanceof At) {
381                                                param = new AtParameter();
382                                                ((AtParameter) param).setType(theContext, parameterType, innerCollectionType,
383                                                                outerCollectionType);
384                                        } else if (nextAnnotation instanceof Count) {
385                                                param = new CountParameter();
386                                        } else if (nextAnnotation instanceof Offset) {
387                                                param = new OffsetParameter();
388                                        } else if (nextAnnotation instanceof Sort) {
389                                                param = new SortParameter(theContext);
390                                        } else if (nextAnnotation instanceof TransactionParam) {
391                                                param = new TransactionParameter(theContext);
392                                        } else if (nextAnnotation instanceof ConditionalUrlParam) {
393                                                param = new ConditionalParamBinder(theRestfulOperationTypeEnum,
394                                                                ((ConditionalUrlParam) nextAnnotation).supportsMultiple());
395                                        } else if (nextAnnotation instanceof OperationParam) {
396                                                Operation op = theMethod.getAnnotation(Operation.class);
397                                                param = new OperationParameter(theContext, op.name(), ((OperationParam) nextAnnotation));
398                                        } else if (nextAnnotation instanceof Validate.Mode) {
399                                                if (parameterType.equals(ValidationModeEnum.class) == false) {
400                                                        throw new ConfigurationException(Msg.code(1436) + "Parameter annotated with @"
401                                                                        + Validate.class.getSimpleName() + "." + Validate.Mode.class.getSimpleName()
402                                                                        + " must be of type " + ValidationModeEnum.class.getName());
403                                                }
404                                                param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE,
405                                                                Constants.EXTOP_VALIDATE_MODE, 0, 1).setConverter(new IOperationParamConverter() {
406                                                                        @Override
407                                                                        public Object outgoingClient(Object theObject) {
408                                                                                return ParametersUtil.createString(theContext,
409                                                                                                ((ValidationModeEnum) theObject).getCode());
410                                                                        }
411                                                                });
412                                        } else if (nextAnnotation instanceof Validate.Profile) {
413                                                if (parameterType.equals(String.class) == false) {
414                                                        throw new ConfigurationException(Msg.code(1437) + "Parameter annotated with @"
415                                                                        + Validate.class.getSimpleName() + "." + Validate.Profile.class.getSimpleName()
416                                                                        + " must be of type " + String.class.getName());
417                                                }
418                                                param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE,
419                                                                Constants.EXTOP_VALIDATE_PROFILE, 0, 1).setConverter(new IOperationParamConverter() {
420
421                                                                        @Override
422                                                                        public Object outgoingClient(Object theObject) {
423                                                                                return ParametersUtil.createString(theContext, theObject.toString());
424                                                                        }
425                                                                });
426                                        } else {
427                                                continue;
428                                        }
429
430                                }
431
432                        }
433
434                        if (param == null) {
435                                throw new ConfigurationException(Msg.code(1438) + "Parameter #" + ((paramIndex + 1)) + "/" + (parameterTypes.length)
436                                                + " of method '" + theMethod.getName() + "' on type '"
437                                                + theMethod.getDeclaringClass().getCanonicalName()
438                                                + "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter");
439                        }
440
441                        param.initializeTypes(theMethod, outerCollectionType, innerCollectionType, parameterType);
442                        parameters.add(param);
443
444                        paramIndex++;
445                }
446                return parameters;
447        }
448
449        public static void parseClientRequestResourceHeaders(IIdType theRequestedId, Map<String, List<String>> theHeaders,
450                        IBaseResource resource) {
451                List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
452                if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
453                        String headerValue = lmHeaders.get(0);
454                        Date headerDateValue;
455                        try {
456                                headerDateValue = DateUtils.parseDate(headerValue);
457                                if (resource instanceof IResource) {
458                                        IResource iResource = (IResource) resource;
459                                        InstantDt existing = ResourceMetadataKeyEnum.UPDATED.get(iResource);
460                                        if (existing == null || existing.isEmpty()) {
461                                                InstantDt lmValue = new InstantDt(headerDateValue);
462                                                iResource.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
463                                        }
464                                } else if (resource instanceof IAnyResource) {
465                                        IAnyResource anyResource = (IAnyResource) resource;
466                                        if (anyResource.getMeta().getLastUpdated() == null) {
467                                                anyResource.getMeta().setLastUpdated(headerDateValue);
468                                        }
469                                }
470                        } catch (Exception e) {
471                                ourLog.warn("Unable to parse date string '{}'. Error is: {}", headerValue, e.toString());
472                        }
473                }
474
475                List<String> clHeaders = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
476                if (clHeaders != null && clHeaders.size() > 0 && StringUtils.isNotBlank(clHeaders.get(0))) {
477                        String headerValue = clHeaders.get(0);
478                        if (isNotBlank(headerValue)) {
479                                new IdDt(headerValue).applyTo(resource);
480                        }
481                }
482
483                List<String> locationHeaders = theHeaders.get(Constants.HEADER_LOCATION_LC);
484                if (locationHeaders != null && locationHeaders.size() > 0 && StringUtils.isNotBlank(locationHeaders.get(0))) {
485                        String headerValue = locationHeaders.get(0);
486                        if (isNotBlank(headerValue)) {
487                                new IdDt(headerValue).applyTo(resource);
488                        }
489                }
490
491                IdDt existing = IdDt.of(resource);
492
493                List<String> eTagHeaders = theHeaders.get(Constants.HEADER_ETAG_LC);
494                String eTagVersion = null;
495                if (eTagHeaders != null && eTagHeaders.size() > 0) {
496                        eTagVersion = ParameterUtil.parseETagValue(eTagHeaders.get(0));
497                }
498                if (isNotBlank(eTagVersion)) {
499                        if (existing == null || existing.isEmpty()) {
500                                if (theRequestedId != null) {
501                                        theRequestedId.withVersion(eTagVersion).applyTo(resource);
502                                }
503                        } else if (existing.hasVersionIdPart() == false) {
504                                existing.withVersion(eTagVersion).applyTo(resource);
505                        }
506                } else if (existing == null || existing.isEmpty()) {
507                        if (theRequestedId != null) {
508                                theRequestedId.applyTo(resource);
509                        }
510                }
511
512        }
513
514        public static MethodOutcome process2xxResponse(FhirContext theContext, int theResponseStatusCode,
515                        String theResponseMimeType, InputStream theResponseReader, Map<String, List<String>> theHeaders) {
516                List<String> locationHeaders = new ArrayList<>();
517                List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC);
518                if (lh != null) {
519                        locationHeaders.addAll(lh);
520                }
521                List<String> clh = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
522                if (clh != null) {
523                        locationHeaders.addAll(clh);
524                }
525
526                MethodOutcome retVal = new MethodOutcome();
527                if (locationHeaders.size() > 0) {
528                        String locationHeader = locationHeaders.get(0);
529                        BaseOutcomeReturningMethodBinding.parseContentLocation(theContext, retVal, locationHeader);
530                }
531                if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) {
532                        EncodingEnum ct = EncodingEnum.forContentType(theResponseMimeType);
533                        if (ct != null) {
534                                PushbackInputStream reader = new PushbackInputStream(theResponseReader);
535
536                                try {
537                                        int firstByte = reader.read();
538                                        if (firstByte == -1) {
539                                                BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read");
540                                                reader = null;
541                                        } else {
542                                                reader.unread(firstByte);
543                                        }
544                                } catch (IOException e) {
545                                        BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read", e);
546                                        reader = null;
547                                }
548
549                                if (reader != null) {
550                                        IParser parser = ct.newParser(theContext);
551                                        IBaseResource outcome = parser.parseResource(reader);
552                                        if (outcome instanceof IBaseOperationOutcome) {
553                                                retVal.setOperationOutcome((IBaseOperationOutcome) outcome);
554                                        } else {
555                                                retVal.setResource(outcome);
556                                        }
557                                }
558
559                        } else {
560                                BaseOutcomeReturningMethodBinding.ourLog.debug("Ignoring response content of type: {}",
561                                                theResponseMimeType);
562                        }
563                }
564                return retVal;
565        }
566
567}