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