001/* 002 * #%L 003 * HAPI FHIR - Client Framework 004 * %% 005 * Copyright (C) 2014 - 2024 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( 096 EncodingEnum theEncoding, IHttpRequest theHttpRequest, 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 public static HttpGetClientInvocation createConformanceInvocation(FhirContext theContext) { 119 return new HttpGetClientInvocation(theContext, "metadata"); 120 } 121 122 public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, FhirContext theContext) { 123 return createCreateInvocation(theResource, null, theContext); 124 } 125 126 public static HttpPostClientInvocation createCreateInvocation( 127 IBaseResource theResource, String theResourceBody, FhirContext theContext) { 128 RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource); 129 String resourceName = def.getName(); 130 131 StringBuilder urlExtension = new StringBuilder(); 132 urlExtension.append(resourceName); 133 134 HttpPostClientInvocation retVal; 135 if (StringUtils.isBlank(theResourceBody)) { 136 retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString()); 137 } else { 138 retVal = new HttpPostClientInvocation(theContext, theResourceBody, false, urlExtension.toString()); 139 } 140 141 retVal.setOmitResourceId(true); 142 143 return retVal; 144 } 145 146 public static HttpPostClientInvocation createCreateInvocation( 147 IBaseResource theResource, 148 String theResourceBody, 149 FhirContext theContext, 150 Map<String, List<String>> theIfNoneExistParams) { 151 HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theContext); 152 retVal.setIfNoneExistParams(theIfNoneExistParams); 153 return retVal; 154 } 155 156 public static HttpPostClientInvocation createCreateInvocation( 157 IBaseResource theResource, String theResourceBody, FhirContext theContext, String theIfNoneExistUrl) { 158 HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theContext); 159 retVal.setIfNoneExistString(theIfNoneExistUrl); 160 return retVal; 161 } 162 163 public static HttpPatchClientInvocation createPatchInvocation( 164 FhirContext theContext, IIdType theId, PatchTypeEnum thePatchType, String theBody) { 165 return PatchMethodBinding.createPatchInvocation(theContext, theId, thePatchType, theBody); 166 } 167 168 public static HttpPatchClientInvocation createPatchInvocation( 169 FhirContext theContext, 170 PatchTypeEnum thePatchType, 171 String theBody, 172 String theResourceType, 173 Map<String, List<String>> theMatchParams) { 174 return PatchMethodBinding.createPatchInvocation( 175 theContext, thePatchType, theBody, theResourceType, theMatchParams); 176 } 177 178 public static HttpPatchClientInvocation createPatchInvocation( 179 FhirContext theContext, String theUrl, PatchTypeEnum thePatchType, String theBody) { 180 return PatchMethodBinding.createPatchInvocation(theContext, theUrl, thePatchType, theBody); 181 } 182 183 public static HttpPutClientInvocation createUpdateInvocation( 184 FhirContext theContext, 185 IBaseResource theResource, 186 String theResourceBody, 187 Map<String, List<String>> theMatchParams) { 188 String resourceType = theContext.getResourceType(theResource); 189 190 StringBuilder b = createUrl(resourceType, theMatchParams); 191 192 HttpPutClientInvocation retVal; 193 if (StringUtils.isBlank(theResourceBody)) { 194 retVal = new HttpPutClientInvocation(theContext, theResource, b.toString()); 195 } else { 196 retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, b.toString()); 197 } 198 199 return retVal; 200 } 201 202 public static HttpPutClientInvocation createUpdateInvocation( 203 FhirContext theContext, IBaseResource theResource, String theResourceBody, String theMatchUrl) { 204 HttpPutClientInvocation retVal; 205 if (StringUtils.isBlank(theResourceBody)) { 206 retVal = new HttpPutClientInvocation(theContext, theResource, theMatchUrl); 207 } else { 208 retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, theMatchUrl); 209 } 210 211 return retVal; 212 } 213 214 public static HttpPutClientInvocation createUpdateInvocation( 215 IBaseResource theResource, String theResourceBody, IIdType theId, FhirContext theContext) { 216 String resourceName = theContext.getResourceType(theResource); 217 StringBuilder urlBuilder = new StringBuilder(); 218 urlBuilder.append(resourceName); 219 urlBuilder.append('/'); 220 urlBuilder.append(theId.getIdPart()); 221 String urlExtension = urlBuilder.toString(); 222 223 HttpPutClientInvocation retVal; 224 if (StringUtils.isBlank(theResourceBody)) { 225 retVal = new HttpPutClientInvocation(theContext, theResource, urlExtension); 226 } else { 227 retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, urlExtension); 228 } 229 230 retVal.setForceResourceId(theId); 231 232 if (theId.hasVersionIdPart()) { 233 retVal.addHeader(Constants.HEADER_IF_MATCH, '"' + theId.getVersionIdPart() + '"'); 234 } 235 236 return retVal; 237 } 238 239 public static HttpPutClientInvocation createUpdateHistoryRewriteInvocation( 240 IBaseResource theResource, String theResourceBody, IIdType theId, FhirContext theContext) { 241 String resourceName = theContext.getResourceType(theResource); 242 StringBuilder urlBuilder = new StringBuilder(); 243 urlBuilder.append(resourceName); 244 urlBuilder.append('/'); 245 urlBuilder.append(theId.getIdPart()); 246 if (theId.hasVersionIdPart()) { 247 urlBuilder.append('/'); 248 urlBuilder.append(Constants.PARAM_HISTORY); 249 urlBuilder.append('/'); 250 urlBuilder.append(theId.getVersionIdPart()); 251 } 252 253 String urlExtension = urlBuilder.toString(); 254 255 HttpPutClientInvocation retVal; 256 if (StringUtils.isBlank(theResourceBody)) { 257 retVal = new HttpPutClientInvocation(theContext, theResource, urlExtension); 258 } else { 259 retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, urlExtension); 260 } 261 262 return retVal; 263 } 264 265 public static StringBuilder createUrl(String theResourceType, Map<String, List<String>> theMatchParams) { 266 StringBuilder b = new StringBuilder(); 267 268 b.append(theResourceType); 269 270 boolean haveQuestionMark = false; 271 for (Entry<String, List<String>> nextEntry : theMatchParams.entrySet()) { 272 for (String nextValue : nextEntry.getValue()) { 273 b.append(haveQuestionMark ? '&' : '?'); 274 haveQuestionMark = true; 275 b.append(UrlUtil.escapeUrlParam(nextEntry.getKey())); 276 b.append('='); 277 b.append(UrlUtil.escapeUrlParam(nextValue)); 278 } 279 } 280 return b; 281 } 282 283 @SuppressWarnings("unchecked") 284 public static List<IParameter> getResourceParameters( 285 final FhirContext theContext, 286 Method theMethod, 287 Object theProvider, 288 RestOperationTypeEnum theRestfulOperationTypeEnum) { 289 List<IParameter> parameters = new ArrayList<>(); 290 291 Class<?>[] parameterTypes = theMethod.getParameterTypes(); 292 int paramIndex = 0; 293 for (Annotation[] annotations : theMethod.getParameterAnnotations()) { 294 295 IParameter param = null; 296 Class<?> parameterType = parameterTypes[paramIndex]; 297 Class<? extends java.util.Collection<?>> outerCollectionType = null; 298 Class<? extends java.util.Collection<?>> innerCollectionType = null; 299 if (TagList.class.isAssignableFrom(parameterType)) { 300 // TagList is handled directly within the method bindings 301 param = new NullParameter(); 302 } else { 303 if (Collection.class.isAssignableFrom(parameterType)) { 304 innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType; 305 parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex); 306 } 307 if (Collection.class.isAssignableFrom(parameterType)) { 308 outerCollectionType = innerCollectionType; 309 innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType; 310 parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex); 311 } 312 if (Collection.class.isAssignableFrom(parameterType)) { 313 throw new ConfigurationException( 314 Msg.code(1433) + "Argument #" + paramIndex + " of Method '" + theMethod.getName() 315 + "' in type '" 316 + theMethod.getDeclaringClass().getCanonicalName() 317 + "' is of an invalid generic type (can not be a collection of a collection of a collection)"); 318 } 319 } 320 321 if (parameterType.equals(SummaryEnum.class)) { 322 param = new SummaryEnumParameter(); 323 } else if (parameterType.equals(PatchTypeEnum.class)) { 324 param = new PatchTypeParameter(); 325 } else { 326 for (int i = 0; i < annotations.length && param == null; i++) { 327 Annotation nextAnnotation = annotations[i]; 328 329 if (nextAnnotation instanceof RequiredParam) { 330 SearchParameter parameter = new SearchParameter(); 331 parameter.setName(((RequiredParam) nextAnnotation).name()); 332 parameter.setRequired(true); 333 parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes()); 334 parameter.setCompositeTypes(((RequiredParam) nextAnnotation).compositeTypes()); 335 parameter.setChainlists(((RequiredParam) nextAnnotation).chainWhitelist()); 336 parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType); 337 param = parameter; 338 } else if (nextAnnotation instanceof OptionalParam) { 339 SearchParameter parameter = new SearchParameter(); 340 parameter.setName(((OptionalParam) nextAnnotation).name()); 341 parameter.setRequired(false); 342 parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes()); 343 parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes()); 344 parameter.setChainlists(((OptionalParam) nextAnnotation).chainWhitelist()); 345 parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType); 346 param = parameter; 347 } else if (nextAnnotation instanceof RawParam) { 348 param = new RawParamsParmeter(); 349 } else if (nextAnnotation instanceof IncludeParam) { 350 Class<? extends Collection<Include>> instantiableCollectionType; 351 Class<?> specType; 352 353 if (parameterType == String.class) { 354 instantiableCollectionType = null; 355 specType = String.class; 356 } else if ((parameterType != Include.class) 357 || innerCollectionType == null 358 || outerCollectionType != null) { 359 throw new ConfigurationException(Msg.code(1434) + "Method '" + theMethod.getName() 360 + "' is annotated with @" 361 + IncludeParam.class.getSimpleName() + " but has a type other than Collection<" 362 + Include.class.getSimpleName() + ">"); 363 } else { 364 instantiableCollectionType = (Class<? extends Collection<Include>>) 365 CollectionBinder.getInstantiableCollectionType( 366 innerCollectionType, "Method '" + theMethod.getName() + "'"); 367 specType = parameterType; 368 } 369 370 param = new IncludeParameter( 371 (IncludeParam) nextAnnotation, instantiableCollectionType, specType); 372 } else if (nextAnnotation instanceof ResourceParam) { 373 if (IBaseResource.class.isAssignableFrom(parameterType)) { 374 // good 375 } else if (String.class.equals(parameterType)) { 376 // good 377 } else { 378 StringBuilder b = new StringBuilder(); 379 b.append("Method '"); 380 b.append(theMethod.getName()); 381 b.append("' is annotated with @"); 382 b.append(ResourceParam.class.getSimpleName()); 383 b.append(" but has a type that is not an implementation of "); 384 b.append(IBaseResource.class.getCanonicalName()); 385 throw new ConfigurationException(Msg.code(1435) + b.toString()); 386 } 387 param = new ResourceParameter(parameterType); 388 } else if (nextAnnotation instanceof IdParam) { 389 param = new NullParameter(); 390 } else if (nextAnnotation instanceof Elements) { 391 param = new ElementsParameter(); 392 } else if (nextAnnotation instanceof Since) { 393 param = new SinceParameter(); 394 ((SinceParameter) param) 395 .setType(theContext, parameterType, innerCollectionType, outerCollectionType); 396 } else if (nextAnnotation instanceof At) { 397 param = new AtParameter(); 398 ((AtParameter) param) 399 .setType(theContext, parameterType, innerCollectionType, outerCollectionType); 400 } else if (nextAnnotation instanceof Count) { 401 param = new CountParameter(); 402 } else if (nextAnnotation instanceof Offset) { 403 param = new OffsetParameter(); 404 } else if (nextAnnotation instanceof Sort) { 405 param = new SortParameter(theContext); 406 } else if (nextAnnotation instanceof TransactionParam) { 407 param = new TransactionParameter(theContext); 408 } else if (nextAnnotation instanceof ConditionalUrlParam) { 409 param = new ConditionalParamBinder( 410 theRestfulOperationTypeEnum, ((ConditionalUrlParam) nextAnnotation).supportsMultiple()); 411 } else if (nextAnnotation instanceof OperationParam) { 412 Operation op = theMethod.getAnnotation(Operation.class); 413 param = new OperationParameter(theContext, op.name(), ((OperationParam) nextAnnotation)); 414 } else if (nextAnnotation instanceof Validate.Mode) { 415 if (parameterType.equals(ValidationModeEnum.class) == false) { 416 throw new ConfigurationException(Msg.code(1436) + "Parameter annotated with @" 417 + Validate.class.getSimpleName() + "." + Validate.Mode.class.getSimpleName() 418 + " must be of type " + ValidationModeEnum.class.getName()); 419 } 420 param = new OperationParameter( 421 theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_MODE, 0, 1) 422 .setConverter(new IOperationParamConverter() { 423 @Override 424 public Object outgoingClient(Object theObject) { 425 return ParametersUtil.createString( 426 theContext, ((ValidationModeEnum) theObject).getCode()); 427 } 428 }); 429 } else if (nextAnnotation instanceof Validate.Profile) { 430 if (parameterType.equals(String.class) == false) { 431 throw new ConfigurationException(Msg.code(1437) + "Parameter annotated with @" 432 + Validate.class.getSimpleName() + "." + Validate.Profile.class.getSimpleName() 433 + " must be of type " + String.class.getName()); 434 } 435 param = new OperationParameter( 436 theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_PROFILE, 0, 1) 437 .setConverter(new IOperationParamConverter() { 438 439 @Override 440 public Object outgoingClient(Object theObject) { 441 return ParametersUtil.createString(theContext, theObject.toString()); 442 } 443 }); 444 } else { 445 continue; 446 } 447 } 448 } 449 450 if (param == null) { 451 throw new ConfigurationException( 452 Msg.code(1438) + "Parameter #" + ((paramIndex + 1)) + "/" + (parameterTypes.length) 453 + " of method '" + theMethod.getName() + "' on type '" 454 + theMethod.getDeclaringClass().getCanonicalName() 455 + "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter"); 456 } 457 458 param.initializeTypes(theMethod, outerCollectionType, innerCollectionType, parameterType); 459 parameters.add(param); 460 461 paramIndex++; 462 } 463 return parameters; 464 } 465 466 public static void parseClientRequestResourceHeaders( 467 IIdType theRequestedId, Map<String, List<String>> theHeaders, IBaseResource resource) { 468 List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE); 469 if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) { 470 String headerValue = lmHeaders.get(0); 471 Date headerDateValue; 472 try { 473 headerDateValue = DateUtils.parseDate(headerValue); 474 if (resource instanceof IResource) { 475 IResource iResource = (IResource) resource; 476 InstantDt existing = ResourceMetadataKeyEnum.UPDATED.get(iResource); 477 if (existing == null || existing.isEmpty()) { 478 InstantDt lmValue = new InstantDt(headerDateValue); 479 iResource.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue); 480 } 481 } else if (resource instanceof IAnyResource) { 482 IAnyResource anyResource = (IAnyResource) resource; 483 if (anyResource.getMeta().getLastUpdated() == null) { 484 anyResource.getMeta().setLastUpdated(headerDateValue); 485 } 486 } 487 } catch (Exception e) { 488 ourLog.warn("Unable to parse date string '{}'. Error is: {}", headerValue, e.toString()); 489 } 490 } 491 492 List<String> clHeaders = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC); 493 if (clHeaders != null && clHeaders.size() > 0 && StringUtils.isNotBlank(clHeaders.get(0))) { 494 String headerValue = clHeaders.get(0); 495 if (isNotBlank(headerValue)) { 496 new IdDt(headerValue).applyTo(resource); 497 } 498 } 499 500 List<String> locationHeaders = theHeaders.get(Constants.HEADER_LOCATION_LC); 501 if (locationHeaders != null && locationHeaders.size() > 0 && StringUtils.isNotBlank(locationHeaders.get(0))) { 502 String headerValue = locationHeaders.get(0); 503 if (isNotBlank(headerValue)) { 504 new IdDt(headerValue).applyTo(resource); 505 } 506 } 507 508 IdDt existing = IdDt.of(resource); 509 510 List<String> eTagHeaders = theHeaders.get(Constants.HEADER_ETAG_LC); 511 String eTagVersion = null; 512 if (eTagHeaders != null && eTagHeaders.size() > 0) { 513 eTagVersion = ParameterUtil.parseETagValue(eTagHeaders.get(0)); 514 } 515 if (isNotBlank(eTagVersion)) { 516 if (existing == null || existing.isEmpty()) { 517 if (theRequestedId != null) { 518 theRequestedId.withVersion(eTagVersion).applyTo(resource); 519 } 520 } else if (existing.hasVersionIdPart() == false) { 521 existing.withVersion(eTagVersion).applyTo(resource); 522 } 523 } else if (existing == null || existing.isEmpty()) { 524 if (theRequestedId != null) { 525 theRequestedId.applyTo(resource); 526 } 527 } 528 } 529 530 public static MethodOutcome process2xxResponse( 531 FhirContext theContext, 532 int theResponseStatusCode, 533 String theResponseMimeType, 534 InputStream theResponseReader, 535 Map<String, List<String>> theHeaders) { 536 List<String> locationHeaders = new ArrayList<>(); 537 List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC); 538 if (lh != null) { 539 locationHeaders.addAll(lh); 540 } 541 List<String> clh = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC); 542 if (clh != null) { 543 locationHeaders.addAll(clh); 544 } 545 546 MethodOutcome retVal = new MethodOutcome(); 547 retVal.setResponseStatusCode(theResponseStatusCode); 548 if (locationHeaders.size() > 0) { 549 String locationHeader = locationHeaders.get(0); 550 BaseOutcomeReturningMethodBinding.parseContentLocation(theContext, retVal, locationHeader); 551 } 552 if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) { 553 EncodingEnum ct = EncodingEnum.forContentType(theResponseMimeType); 554 if (ct != null) { 555 PushbackInputStream reader = new PushbackInputStream(theResponseReader); 556 557 try { 558 int firstByte = reader.read(); 559 if (firstByte == -1) { 560 BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read"); 561 reader = null; 562 } else { 563 reader.unread(firstByte); 564 } 565 } catch (IOException e) { 566 BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read", e); 567 reader = null; 568 } 569 570 if (reader != null) { 571 IParser parser = ct.newParser(theContext); 572 IBaseResource outcome = parser.parseResource(reader); 573 if (outcome instanceof IBaseOperationOutcome) { 574 retVal.setOperationOutcome((IBaseOperationOutcome) outcome); 575 } else { 576 retVal.setResource(outcome); 577 } 578 } 579 580 } else { 581 BaseOutcomeReturningMethodBinding.ourLog.debug( 582 "Ignoring response content of type: {}", theResponseMimeType); 583 } 584 } 585 return retVal; 586 } 587}