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