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