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 - 2018 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        private static final Set<String> ourServletRequestTypes = new HashSet<String>();
053        private static final Set<String> ourServletResponseTypes = new HashSet<String>();
054
055        static {
056                ourServletRequestTypes.add("javax.servlet.ServletRequest");
057                ourServletResponseTypes.add("javax.servlet.ServletResponse");
058                ourServletRequestTypes.add("javax.servlet.http.HttpServletRequest");
059                ourServletResponseTypes.add("javax.servlet.http.HttpServletResponse");
060        }
061
062        /** Non instantiable */
063        private MethodUtil() {
064                // nothing
065        }
066
067        public static void addAcceptHeaderToRequest(EncodingEnum theEncoding, IHttpRequest theHttpRequest,
068                        FhirContext theContext) {
069                if (theEncoding == null) {
070                        if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2_1) == false) {
071                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY);
072                        } else {
073                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY);
074                        }
075                } else if (theEncoding == EncodingEnum.JSON) {
076                        if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2_1) == false) {
077                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
078                        } else {
079                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY);
080                        }
081                } else if (theEncoding == EncodingEnum.XML) {
082                        if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2_1) == false) {
083                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
084                        } else {
085                                theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY);
086                        }
087                }
088
089        }
090
091        public static HttpGetClientInvocation createConformanceInvocation(FhirContext theContext) {
092                return new HttpGetClientInvocation(theContext, "metadata");
093        }
094
095        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, FhirContext theContext) {
096                return createCreateInvocation(theResource, null, theContext);
097        }
098
099        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, String theResourceBody,
100                        FhirContext theContext) {
101                RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
102                String resourceName = def.getName();
103
104                StringBuilder urlExtension = new StringBuilder();
105                urlExtension.append(resourceName);
106
107                HttpPostClientInvocation retVal;
108                if (StringUtils.isBlank(theResourceBody)) {
109                        retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString());
110                } else {
111                        retVal = new HttpPostClientInvocation(theContext, theResourceBody, false, urlExtension.toString());
112                }
113
114                retVal.setOmitResourceId(true);
115
116                return retVal;
117        }
118
119        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, String theResourceBody,
120                        FhirContext theContext, Map<String, List<String>> theIfNoneExistParams) {
121                HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theContext);
122                retVal.setIfNoneExistParams(theIfNoneExistParams);
123                return retVal;
124        }
125
126        public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, String theResourceBody,
127                        FhirContext theContext, String theIfNoneExistUrl) {
128                HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theContext);
129                retVal.setIfNoneExistString(theIfNoneExistUrl);
130                return retVal;
131        }
132
133        public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, IIdType theId,
134                        PatchTypeEnum thePatchType, String theBody) {
135                return PatchMethodBinding.createPatchInvocation(theContext, theId, thePatchType, theBody);
136        }
137
138        public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, PatchTypeEnum thePatchType,
139                        String theBody, String theResourceType, Map<String, List<String>> theMatchParams) {
140                return PatchMethodBinding.createPatchInvocation(theContext, thePatchType, theBody, theResourceType,
141                                theMatchParams);
142        }
143
144        public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, String theUrl,
145                        PatchTypeEnum thePatchType, String theBody) {
146                return PatchMethodBinding.createPatchInvocation(theContext, theUrl, thePatchType, theBody);
147        }
148
149        public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource,
150                        String theResourceBody, Map<String, List<String>> theMatchParams) {
151                String resourceType = theContext.getResourceDefinition(theResource).getName();
152
153                StringBuilder b = createUrl(resourceType, theMatchParams);
154
155                HttpPutClientInvocation retVal;
156                if (StringUtils.isBlank(theResourceBody)) {
157                        retVal = new HttpPutClientInvocation(theContext, theResource, b.toString());
158                } else {
159                        retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, b.toString());
160                }
161
162                return retVal;
163        }
164
165        public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource,
166                        String theResourceBody, String theMatchUrl) {
167                HttpPutClientInvocation retVal;
168                if (StringUtils.isBlank(theResourceBody)) {
169                        retVal = new HttpPutClientInvocation(theContext, theResource, theMatchUrl);
170                } else {
171                        retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, theMatchUrl);
172                }
173
174                return retVal;
175        }
176
177        public static HttpPutClientInvocation createUpdateInvocation(IBaseResource theResource, String theResourceBody,
178                        IIdType theId, FhirContext theContext) {
179                String resourceName = theContext.getResourceDefinition(theResource).getName();
180                StringBuilder urlBuilder = new StringBuilder();
181                urlBuilder.append(resourceName);
182                urlBuilder.append('/');
183                urlBuilder.append(theId.getIdPart());
184                String urlExtension = urlBuilder.toString();
185
186                HttpPutClientInvocation retVal;
187                if (StringUtils.isBlank(theResourceBody)) {
188                        retVal = new HttpPutClientInvocation(theContext, theResource, urlExtension);
189                } else {
190                        retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, urlExtension);
191                }
192
193                retVal.setForceResourceId(theId);
194
195                if (theId.hasVersionIdPart()) {
196                        retVal.addHeader(Constants.HEADER_IF_MATCH, '"' + theId.getVersionIdPart() + '"');
197                }
198
199                return retVal;
200        }
201
202        public static StringBuilder createUrl(String theResourceType, Map<String, List<String>> theMatchParams) {
203                StringBuilder b = new StringBuilder();
204
205                b.append(theResourceType);
206
207                boolean haveQuestionMark = false;
208                for (Entry<String, List<String>> nextEntry : theMatchParams.entrySet()) {
209                        for (String nextValue : nextEntry.getValue()) {
210                                b.append(haveQuestionMark ? '&' : '?');
211                                haveQuestionMark = true;
212                                b.append(UrlUtil.escapeUrlParam(nextEntry.getKey()));
213                                b.append('=');
214                                b.append(UrlUtil.escapeUrlParam(nextValue));
215                        }
216                }
217                return b;
218        }
219
220        public static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
221                for (Annotation annotation : theAnnotations) {
222                        if (annotation instanceof Description) {
223                                Description desc = (Description) annotation;
224                                if (isNotBlank(desc.formalDefinition())) {
225                                        theParameter.setDescription(desc.formalDefinition());
226                                } else {
227                                        theParameter.setDescription(desc.shortDefinition());
228                                }
229                        }
230                }
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<IParameter>();
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("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                                                                ((RequiredParam) nextAnnotation).chainBlacklist());
282                                                parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
283                                                MethodUtil.extractDescription(parameter, annotations);
284                                                param = parameter;
285                                        } else if (nextAnnotation instanceof OptionalParam) {
286                                                SearchParameter parameter = new SearchParameter();
287                                                parameter.setName(((OptionalParam) nextAnnotation).name());
288                                                parameter.setRequired(false);
289                                                parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes());
290                                                parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes());
291                                                parameter.setChainlists(((OptionalParam) nextAnnotation).chainWhitelist(),
292                                                                ((OptionalParam) nextAnnotation).chainBlacklist());
293                                                parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
294                                                MethodUtil.extractDescription(parameter, annotations);
295                                                param = parameter;
296                                        } else if (nextAnnotation instanceof RawParam) {
297                                                param = new RawParamsParmeter();
298                                        } else if (nextAnnotation instanceof IncludeParam) {
299                                                Class<? extends Collection<Include>> instantiableCollectionType;
300                                                Class<?> specType;
301
302                                                if (parameterType == String.class) {
303                                                        instantiableCollectionType = null;
304                                                        specType = String.class;
305                                                } else if ((parameterType != Include.class) || innerCollectionType == null
306                                                                || outerCollectionType != null) {
307                                                        throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @"
308                                                                        + IncludeParam.class.getSimpleName() + " but has a type other than Collection<"
309                                                                        + Include.class.getSimpleName() + ">");
310                                                } else {
311                                                        instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder
312                                                                        .getInstantiableCollectionType(innerCollectionType,
313                                                                                        "Method '" + theMethod.getName() + "'");
314                                                        specType = parameterType;
315                                                }
316
317                                                param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType,
318                                                                specType);
319                                        } else if (nextAnnotation instanceof ResourceParam) {
320                                                if (IBaseResource.class.isAssignableFrom(parameterType)) {
321                                                        // good
322                                                } else if (String.class.equals(parameterType)) {
323                                                        // good
324                                                } else if (byte[].class.equals(parameterType)) {
325                                                        // good
326                                                } else if (EncodingEnum.class.equals(parameterType)) {
327                                                        // good
328                                                } else {
329                                                        StringBuilder b = new StringBuilder();
330                                                        b.append("Method '");
331                                                        b.append(theMethod.getName());
332                                                        b.append("' is annotated with @");
333                                                        b.append(ResourceParam.class.getSimpleName());
334                                                        b.append(" but has a type that is not an implemtation of ");
335                                                        b.append(IBaseResource.class.getCanonicalName());
336                                                        b.append(" or String or byte[]");
337                                                        throw new ConfigurationException(b.toString());
338                                                }
339                                                param = new ResourceParameter(parameterType);
340                                        } else if (nextAnnotation instanceof IdParam) {
341                                                param = new NullParameter();
342                                        } else if (nextAnnotation instanceof ServerBase) {
343                                                param = new ServerBaseParamBinder();
344                                        } else if (nextAnnotation instanceof Elements) {
345                                                param = new ElementsParameter();
346                                        } else if (nextAnnotation instanceof Since) {
347                                                param = new SinceParameter();
348                                                ((SinceParameter) param).setType(theContext, parameterType, innerCollectionType,
349                                                                outerCollectionType);
350                                        } else if (nextAnnotation instanceof At) {
351                                                param = new AtParameter();
352                                                ((AtParameter) param).setType(theContext, parameterType, innerCollectionType,
353                                                                outerCollectionType);
354                                        } else if (nextAnnotation instanceof Count) {
355                                                param = new CountParameter();
356                                        } else if (nextAnnotation instanceof Sort) {
357                                                param = new SortParameter(theContext);
358                                        } else if (nextAnnotation instanceof TransactionParam) {
359                                                param = new TransactionParameter(theContext);
360                                        } else if (nextAnnotation instanceof ConditionalUrlParam) {
361                                                param = new ConditionalParamBinder(theRestfulOperationTypeEnum,
362                                                                ((ConditionalUrlParam) nextAnnotation).supportsMultiple());
363                                        } else if (nextAnnotation instanceof OperationParam) {
364                                                Operation op = theMethod.getAnnotation(Operation.class);
365                                                param = new OperationParameter(theContext, op.name(), ((OperationParam) nextAnnotation));
366                                        } else if (nextAnnotation instanceof Validate.Mode) {
367                                                if (parameterType.equals(ValidationModeEnum.class) == false) {
368                                                        throw new ConfigurationException("Parameter annotated with @"
369                                                                        + Validate.class.getSimpleName() + "." + Validate.Mode.class.getSimpleName()
370                                                                        + " must be of type " + ValidationModeEnum.class.getName());
371                                                }
372                                                param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE,
373                                                                Constants.EXTOP_VALIDATE_MODE, 0, 1).setConverter(new IOperationParamConverter() {
374                                                                        @Override
375                                                                        public Object incomingServer(Object theObject) {
376                                                                                if (isNotBlank(theObject.toString())) {
377                                                                                        ValidationModeEnum retVal = ValidationModeEnum
378                                                                                                        .forCode(theObject.toString());
379                                                                                        if (retVal == null) {
380                                                                                                OperationParameter.throwInvalidMode(theObject.toString());
381                                                                                        }
382                                                                                        return retVal;
383                                                                                }
384                                                                                return null;
385                                                                        }
386
387                                                                        @Override
388                                                                        public Object outgoingClient(Object theObject) {
389                                                                                return ParametersUtil.createString(theContext,
390                                                                                                ((ValidationModeEnum) theObject).getCode());
391                                                                        }
392                                                                });
393                                        } else if (nextAnnotation instanceof Validate.Profile) {
394                                                if (parameterType.equals(String.class) == false) {
395                                                        throw new ConfigurationException("Parameter annotated with @"
396                                                                        + Validate.class.getSimpleName() + "." + Validate.Profile.class.getSimpleName()
397                                                                        + " must be of type " + String.class.getName());
398                                                }
399                                                param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE,
400                                                                Constants.EXTOP_VALIDATE_PROFILE, 0, 1).setConverter(new IOperationParamConverter() {
401                                                                        @Override
402                                                                        public Object incomingServer(Object theObject) {
403                                                                                return theObject.toString();
404                                                                        }
405
406                                                                        @Override
407                                                                        public Object outgoingClient(Object theObject) {
408                                                                                return ParametersUtil.createString(theContext, theObject.toString());
409                                                                        }
410                                                                });
411                                        } else {
412                                                continue;
413                                        }
414
415                                }
416
417                        }
418
419                        if (param == null) {
420                                throw new ConfigurationException("Parameter #" + ((paramIndex + 1)) + "/" + (parameterTypes.length)
421                                                + " of method '" + theMethod.getName() + "' on type '"
422                                                + theMethod.getDeclaringClass().getCanonicalName()
423                                                + "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter");
424                        }
425
426                        param.initializeTypes(theMethod, outerCollectionType, innerCollectionType, parameterType);
427                        parameters.add(param);
428
429                        paramIndex++;
430                }
431                return parameters;
432        }
433
434        public static void parseClientRequestResourceHeaders(IIdType theRequestedId, Map<String, List<String>> theHeaders,
435                        IBaseResource resource) {
436                List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
437                if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
438                        String headerValue = lmHeaders.get(0);
439                        Date headerDateValue;
440                        try {
441                                headerDateValue = DateUtils.parseDate(headerValue);
442                                if (resource instanceof IResource) {
443                                        IResource iResource = (IResource) resource;
444                                        InstantDt existing = ResourceMetadataKeyEnum.UPDATED.get(iResource);
445                                        if (existing == null || existing.isEmpty()) {
446                                                InstantDt lmValue = new InstantDt(headerDateValue);
447                                                iResource.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
448                                        }
449                                } else if (resource instanceof IAnyResource) {
450                                        IAnyResource anyResource = (IAnyResource) resource;
451                                        if (anyResource.getMeta().getLastUpdated() == null) {
452                                                anyResource.getMeta().setLastUpdated(headerDateValue);
453                                        }
454                                }
455                        } catch (Exception e) {
456                                ourLog.warn("Unable to parse date string '{}'. Error is: {}", headerValue, e.toString());
457                        }
458                }
459
460                List<String> clHeaders = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
461                if (clHeaders != null && clHeaders.size() > 0 && StringUtils.isNotBlank(clHeaders.get(0))) {
462                        String headerValue = clHeaders.get(0);
463                        if (isNotBlank(headerValue)) {
464                                new IdDt(headerValue).applyTo(resource);
465                        }
466                }
467
468                List<String> locationHeaders = theHeaders.get(Constants.HEADER_LOCATION_LC);
469                if (locationHeaders != null && locationHeaders.size() > 0 && StringUtils.isNotBlank(locationHeaders.get(0))) {
470                        String headerValue = locationHeaders.get(0);
471                        if (isNotBlank(headerValue)) {
472                                new IdDt(headerValue).applyTo(resource);
473                        }
474                }
475
476                IdDt existing = IdDt.of(resource);
477
478                List<String> eTagHeaders = theHeaders.get(Constants.HEADER_ETAG_LC);
479                String eTagVersion = null;
480                if (eTagHeaders != null && eTagHeaders.size() > 0) {
481                        eTagVersion = ParameterUtil.parseETagValue(eTagHeaders.get(0));
482                }
483                if (isNotBlank(eTagVersion)) {
484                        if (existing == null || existing.isEmpty()) {
485                                if (theRequestedId != null) {
486                                        theRequestedId.withVersion(eTagVersion).applyTo(resource);
487                                }
488                        } else if (existing.hasVersionIdPart() == false) {
489                                existing.withVersion(eTagVersion).applyTo(resource);
490                        }
491                } else if (existing == null || existing.isEmpty()) {
492                        if (theRequestedId != null) {
493                                theRequestedId.applyTo(resource);
494                        }
495                }
496
497        }
498
499        public static MethodOutcome process2xxResponse(FhirContext theContext, int theResponseStatusCode,
500                        String theResponseMimeType, Reader theResponseReader, Map<String, List<String>> theHeaders) {
501                List<String> locationHeaders = new ArrayList<String>();
502                List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC);
503                if (lh != null) {
504                        locationHeaders.addAll(lh);
505                }
506                List<String> clh = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
507                if (clh != null) {
508                        locationHeaders.addAll(clh);
509                }
510
511                MethodOutcome retVal = new MethodOutcome();
512                if (locationHeaders != null && locationHeaders.size() > 0) {
513                        String locationHeader = locationHeaders.get(0);
514                        BaseOutcomeReturningMethodBinding.parseContentLocation(theContext, retVal, locationHeader);
515                }
516                if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) {
517                        EncodingEnum ct = EncodingEnum.forContentType(theResponseMimeType);
518                        if (ct != null) {
519                                PushbackReader reader = new PushbackReader(theResponseReader);
520
521                                try {
522                                        int firstByte = reader.read();
523                                        if (firstByte == -1) {
524                                                BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read");
525                                                reader = null;
526                                        } else {
527                                                reader.unread(firstByte);
528                                        }
529                                } catch (IOException e) {
530                                        BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read", e);
531                                        reader = null;
532                                }
533
534                                if (reader != null) {
535                                        IParser parser = ct.newParser(theContext);
536                                        IBaseResource outcome = parser.parseResource(reader);
537                                        if (outcome instanceof IBaseOperationOutcome) {
538                                                retVal.setOperationOutcome((IBaseOperationOutcome) outcome);
539                                        } else {
540                                                retVal.setResource(outcome);
541                                        }
542                                }
543
544                        } else {
545                                BaseOutcomeReturningMethodBinding.ourLog.debug("Ignoring response content of type: {}",
546                                                theResponseMimeType);
547                        }
548                }
549                return retVal;
550        }
551
552}