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.impl; 021 022import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 023import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 024import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 025import ca.uhn.fhir.context.FhirContext; 026import ca.uhn.fhir.context.FhirVersionEnum; 027import ca.uhn.fhir.context.IRuntimeDatatypeDefinition; 028import ca.uhn.fhir.context.RuntimeResourceDefinition; 029import ca.uhn.fhir.i18n.Msg; 030import ca.uhn.fhir.model.api.IQueryParameterType; 031import ca.uhn.fhir.model.api.Include; 032import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; 033import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; 034import ca.uhn.fhir.model.primitive.IdDt; 035import ca.uhn.fhir.model.primitive.InstantDt; 036import ca.uhn.fhir.model.primitive.UriDt; 037import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; 038import ca.uhn.fhir.parser.IParser; 039import ca.uhn.fhir.rest.api.CacheControlDirective; 040import ca.uhn.fhir.rest.api.Constants; 041import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum; 042import ca.uhn.fhir.rest.api.EncodingEnum; 043import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; 044import ca.uhn.fhir.rest.api.MethodOutcome; 045import ca.uhn.fhir.rest.api.PagingHttpMethodEnum; 046import ca.uhn.fhir.rest.api.PatchTypeEnum; 047import ca.uhn.fhir.rest.api.PreferReturnEnum; 048import ca.uhn.fhir.rest.api.SearchStyleEnum; 049import ca.uhn.fhir.rest.api.SearchTotalModeEnum; 050import ca.uhn.fhir.rest.api.SortOrderEnum; 051import ca.uhn.fhir.rest.api.SortSpec; 052import ca.uhn.fhir.rest.api.SummaryEnum; 053import ca.uhn.fhir.rest.client.api.IGenericClient; 054import ca.uhn.fhir.rest.client.api.IHttpClient; 055import ca.uhn.fhir.rest.client.api.IHttpRequest; 056import ca.uhn.fhir.rest.client.api.UrlSourceEnum; 057import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; 058import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; 059import ca.uhn.fhir.rest.client.method.DeleteMethodBinding; 060import ca.uhn.fhir.rest.client.method.HistoryMethodBinding; 061import ca.uhn.fhir.rest.client.method.HttpDeleteClientInvocation; 062import ca.uhn.fhir.rest.client.method.HttpGetClientInvocation; 063import ca.uhn.fhir.rest.client.method.HttpSimpleClientInvocation; 064import ca.uhn.fhir.rest.client.method.IClientResponseHandler; 065import ca.uhn.fhir.rest.client.method.MethodUtil; 066import ca.uhn.fhir.rest.client.method.OperationMethodBinding; 067import ca.uhn.fhir.rest.client.method.ReadMethodBinding; 068import ca.uhn.fhir.rest.client.method.SearchMethodBinding; 069import ca.uhn.fhir.rest.client.method.SortParameter; 070import ca.uhn.fhir.rest.client.method.TransactionMethodBinding; 071import ca.uhn.fhir.rest.client.method.ValidateMethodBindingDstu2Plus; 072import ca.uhn.fhir.rest.gclient.IBaseQuery; 073import ca.uhn.fhir.rest.gclient.IClientExecutable; 074import ca.uhn.fhir.rest.gclient.ICreate; 075import ca.uhn.fhir.rest.gclient.ICreateTyped; 076import ca.uhn.fhir.rest.gclient.ICreateWithQuery; 077import ca.uhn.fhir.rest.gclient.ICreateWithQueryTyped; 078import ca.uhn.fhir.rest.gclient.ICriterion; 079import ca.uhn.fhir.rest.gclient.ICriterionInternal; 080import ca.uhn.fhir.rest.gclient.IDelete; 081import ca.uhn.fhir.rest.gclient.IDeleteTyped; 082import ca.uhn.fhir.rest.gclient.IDeleteWithQuery; 083import ca.uhn.fhir.rest.gclient.IDeleteWithQueryTyped; 084import ca.uhn.fhir.rest.gclient.IFetchConformanceTyped; 085import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped; 086import ca.uhn.fhir.rest.gclient.IGetPage; 087import ca.uhn.fhir.rest.gclient.IGetPageTyped; 088import ca.uhn.fhir.rest.gclient.IGetPageUntyped; 089import ca.uhn.fhir.rest.gclient.IHistory; 090import ca.uhn.fhir.rest.gclient.IHistoryTyped; 091import ca.uhn.fhir.rest.gclient.IHistoryUntyped; 092import ca.uhn.fhir.rest.gclient.IMeta; 093import ca.uhn.fhir.rest.gclient.IMetaAddOrDeleteSourced; 094import ca.uhn.fhir.rest.gclient.IMetaAddOrDeleteUnsourced; 095import ca.uhn.fhir.rest.gclient.IMetaGetUnsourced; 096import ca.uhn.fhir.rest.gclient.IOperation; 097import ca.uhn.fhir.rest.gclient.IOperationProcessMsg; 098import ca.uhn.fhir.rest.gclient.IOperationProcessMsgMode; 099import ca.uhn.fhir.rest.gclient.IOperationUnnamed; 100import ca.uhn.fhir.rest.gclient.IOperationUntyped; 101import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput; 102import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; 103import ca.uhn.fhir.rest.gclient.IParam; 104import ca.uhn.fhir.rest.gclient.IPatch; 105import ca.uhn.fhir.rest.gclient.IPatchExecutable; 106import ca.uhn.fhir.rest.gclient.IPatchWithBody; 107import ca.uhn.fhir.rest.gclient.IPatchWithQuery; 108import ca.uhn.fhir.rest.gclient.IPatchWithQueryTyped; 109import ca.uhn.fhir.rest.gclient.IQuery; 110import ca.uhn.fhir.rest.gclient.IRead; 111import ca.uhn.fhir.rest.gclient.IReadExecutable; 112import ca.uhn.fhir.rest.gclient.IReadIfNoneMatch; 113import ca.uhn.fhir.rest.gclient.IReadTyped; 114import ca.uhn.fhir.rest.gclient.ISort; 115import ca.uhn.fhir.rest.gclient.ITransaction; 116import ca.uhn.fhir.rest.gclient.ITransactionTyped; 117import ca.uhn.fhir.rest.gclient.IUntypedQuery; 118import ca.uhn.fhir.rest.gclient.IUpdate; 119import ca.uhn.fhir.rest.gclient.IUpdateExecutable; 120import ca.uhn.fhir.rest.gclient.IUpdateTyped; 121import ca.uhn.fhir.rest.gclient.IUpdateWithQuery; 122import ca.uhn.fhir.rest.gclient.IUpdateWithQueryTyped; 123import ca.uhn.fhir.rest.gclient.IValidate; 124import ca.uhn.fhir.rest.gclient.IValidateUntyped; 125import ca.uhn.fhir.rest.param.DateParam; 126import ca.uhn.fhir.rest.param.DateRangeParam; 127import ca.uhn.fhir.rest.param.TokenParam; 128import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; 129import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 130import ca.uhn.fhir.rest.server.exceptions.NotModifiedException; 131import ca.uhn.fhir.util.ICallable; 132import ca.uhn.fhir.util.ParametersUtil; 133import ca.uhn.fhir.util.UrlUtil; 134import com.google.common.base.Charsets; 135import org.apache.commons.io.IOUtils; 136import org.apache.commons.lang3.StringUtils; 137import org.apache.commons.lang3.Validate; 138import org.hl7.fhir.instance.model.api.IBase; 139import org.hl7.fhir.instance.model.api.IBaseBinary; 140import org.hl7.fhir.instance.model.api.IBaseBundle; 141import org.hl7.fhir.instance.model.api.IBaseConformance; 142import org.hl7.fhir.instance.model.api.IBaseDatatype; 143import org.hl7.fhir.instance.model.api.IBaseMetaType; 144import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; 145import org.hl7.fhir.instance.model.api.IBaseParameters; 146import org.hl7.fhir.instance.model.api.IBaseResource; 147import org.hl7.fhir.instance.model.api.IIdType; 148import org.hl7.fhir.instance.model.api.IPrimitiveType; 149 150import java.io.IOException; 151import java.io.InputStream; 152import java.util.ArrayList; 153import java.util.Arrays; 154import java.util.Collection; 155import java.util.Collections; 156import java.util.Date; 157import java.util.HashMap; 158import java.util.HashSet; 159import java.util.Iterator; 160import java.util.LinkedHashMap; 161import java.util.List; 162import java.util.Map; 163import java.util.Map.Entry; 164import java.util.Objects; 165import java.util.Set; 166 167import static org.apache.commons.lang3.StringUtils.defaultString; 168import static org.apache.commons.lang3.StringUtils.isBlank; 169import static org.apache.commons.lang3.StringUtils.isNotBlank; 170 171/** 172 * @author James Agnew 173 * @author Doug Martin (Regenstrief Center for Biomedical Informatics) 174 */ 175public class GenericClient extends BaseClient implements IGenericClient { 176 177 private static final String I18N_CANNOT_DETEMINE_RESOURCE_TYPE = 178 GenericClient.class.getName() + ".cannotDetermineResourceTypeFromUri"; 179 private static final String I18N_INCOMPLETE_URI_FOR_READ = GenericClient.class.getName() + ".incompleteUriForRead"; 180 private static final String I18N_NO_VERSION_ID_FOR_VREAD = GenericClient.class.getName() + ".noVersionIdForVread"; 181 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class); 182 private FhirContext myContext; 183 private IHttpRequest myLastRequest; 184 private boolean myLogRequestAndResponse; 185 186 /** 187 * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! 188 */ 189 public GenericClient( 190 FhirContext theContext, IHttpClient theHttpClient, String theServerBase, RestfulClientFactory theFactory) { 191 super(theHttpClient, theServerBase, theFactory); 192 myContext = theContext; 193 } 194 195 @Override 196 public IFetchConformanceUntyped capabilities() { 197 return new FetchConformanceInternal(); 198 } 199 200 @Override 201 public ICreate create() { 202 return new CreateInternal(); 203 } 204 205 @Override 206 public IDelete delete() { 207 return new DeleteInternal(); 208 } 209 210 private <T extends IBaseResource> T doReadOrVRead( 211 final Class<T> theType, 212 IIdType theId, 213 boolean theVRead, 214 ICallable<T> theNotModifiedHandler, 215 String theIfVersionMatches, 216 Boolean thePrettyPrint, 217 SummaryEnum theSummary, 218 EncodingEnum theEncoding, 219 Set<String> theSubsetElements, 220 String theCustomAcceptHeaderValue, 221 Map<String, List<String>> theCustomHeaders) { 222 String resName = toResourceName(theType); 223 IIdType id = theId; 224 if (!id.hasBaseUrl()) { 225 id = new IdDt(resName, id.getIdPart(), id.getVersionIdPart()); 226 } 227 228 HttpGetClientInvocation invocation; 229 if (id.hasBaseUrl()) { 230 if (theVRead) { 231 invocation = ReadMethodBinding.createAbsoluteVReadInvocation(getFhirContext(), id); 232 } else { 233 invocation = ReadMethodBinding.createAbsoluteReadInvocation(getFhirContext(), id); 234 } 235 } else { 236 if (theVRead) { 237 invocation = ReadMethodBinding.createVReadInvocation(getFhirContext(), id, resName); 238 } else { 239 invocation = ReadMethodBinding.createReadInvocation(getFhirContext(), id, resName); 240 } 241 } 242 if (isKeepResponses()) { 243 myLastRequest = invocation.asHttpRequest( 244 getServerBase(), createExtraParams(theCustomAcceptHeaderValue), getEncoding(), isPrettyPrint()); 245 } 246 247 if (theIfVersionMatches != null) { 248 invocation.addHeader(Constants.HEADER_IF_NONE_MATCH, '"' + theIfVersionMatches + '"'); 249 } 250 251 boolean allowHtmlResponse = SummaryEnum.TEXT.equals(theSummary); 252 ResourceResponseHandler<T> binding = 253 new ResourceResponseHandler<>(theType, (Class<? extends IBaseResource>) null, id, allowHtmlResponse); 254 255 if (theNotModifiedHandler == null) { 256 return invokeClient( 257 myContext, 258 binding, 259 invocation, 260 theEncoding, 261 thePrettyPrint, 262 myLogRequestAndResponse, 263 theSummary, 264 theSubsetElements, 265 null, 266 theCustomAcceptHeaderValue, 267 theCustomHeaders); 268 } 269 try { 270 return invokeClient( 271 myContext, 272 binding, 273 invocation, 274 theEncoding, 275 thePrettyPrint, 276 myLogRequestAndResponse, 277 theSummary, 278 theSubsetElements, 279 null, 280 theCustomAcceptHeaderValue, 281 theCustomHeaders); 282 } catch (NotModifiedException e) { 283 return theNotModifiedHandler.call(); 284 } 285 } 286 287 @Override 288 public IFetchConformanceUntyped fetchConformance() { 289 return new FetchConformanceInternal(); 290 } 291 292 @Override 293 public void forceConformanceCheck() { 294 super.forceConformanceCheck(); 295 } 296 297 @Override 298 public FhirContext getFhirContext() { 299 return myContext; 300 } 301 302 public IHttpRequest getLastRequest() { 303 return myLastRequest; 304 } 305 306 /** 307 * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! 308 */ 309 public void setLastRequest(IHttpRequest theLastRequest) { 310 myLastRequest = theLastRequest; 311 } 312 313 protected String getPreferredId(IBaseResource theResource, String theId) { 314 if (isNotBlank(theId)) { 315 return theId; 316 } 317 return theResource.getIdElement().getIdPart(); 318 } 319 320 @Override 321 public IHistory history() { 322 return new HistoryInternal(); 323 } 324 325 /** 326 * @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your 327 * client instead, as this provides much more fine-grained control over what is logged. This 328 * method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16) 329 */ 330 @Deprecated 331 public boolean isLogRequestAndResponse() { 332 return myLogRequestAndResponse; 333 } 334 335 @Deprecated // override deprecated method 336 @Override 337 public void setLogRequestAndResponse(boolean theLogRequestAndResponse) { 338 myLogRequestAndResponse = theLogRequestAndResponse; 339 } 340 341 @Override 342 public IGetPage loadPage() { 343 return new LoadPageInternal(); 344 } 345 346 @Override 347 public IMeta meta() { 348 return new MetaInternal(); 349 } 350 351 @Override 352 public IOperation operation() { 353 return new OperationInternal(); 354 } 355 356 @Override 357 public IPatch patch() { 358 return new PatchInternal(); 359 } 360 361 @Override 362 public IRead read() { 363 return new ReadInternal(); 364 } 365 366 @Override 367 public <T extends IBaseResource> T read(Class<T> theType, String theId) { 368 return read(theType, new IdDt(theId)); 369 } 370 371 @Override 372 public <T extends IBaseResource> T read(final Class<T> theType, UriDt theUrl) { 373 IdDt id = theUrl instanceof IdDt ? ((IdDt) theUrl) : new IdDt(theUrl); 374 return doReadOrVRead(theType, id, false, null, null, false, null, null, null, null, null); 375 } 376 377 @Override 378 public IBaseResource read(UriDt theUrl) { 379 IdDt id = new IdDt(theUrl); 380 String resourceType = id.getResourceType(); 381 if (isBlank(resourceType)) { 382 throw new IllegalArgumentException(Msg.code(1365) 383 + myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, theUrl.getValueAsString())); 384 } 385 RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType); 386 if (def == null) { 387 throw new IllegalArgumentException(Msg.code(1366) 388 + myContext 389 .getLocalizer() 390 .getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); 391 } 392 return read(def.getImplementingClass(), id); 393 } 394 395 @SuppressWarnings({"rawtypes", "unchecked"}) 396 @Override 397 public IUntypedQuery search() { 398 return new SearchInternal(); 399 } 400 401 private String toResourceName(Class<? extends IBaseResource> theType) { 402 return myContext.getResourceType(theType); 403 } 404 405 @Override 406 public ITransaction transaction() { 407 return new TransactionInternal(); 408 } 409 410 @Override 411 public IUpdate update() { 412 return new UpdateInternal(); 413 } 414 415 @Override 416 public MethodOutcome update(IdDt theIdDt, IBaseResource theResource) { 417 BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext); 418 if (isKeepResponses()) { 419 myLastRequest = 420 invocation.asHttpRequest(getServerBase(), createExtraParams(null), getEncoding(), isPrettyPrint()); 421 } 422 423 OutcomeResponseHandler binding = new OutcomeResponseHandler(); 424 MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); 425 return resp; 426 } 427 428 @Override 429 public MethodOutcome update(String theId, IBaseResource theResource) { 430 return update(new IdDt(theId), theResource); 431 } 432 433 @Override 434 public IValidate validate() { 435 return new ValidateInternal(); 436 } 437 438 @Override 439 public MethodOutcome validate(IBaseResource theResource) { 440 BaseHttpClientInvocation invocation; 441 invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, theResource); 442 443 if (isKeepResponses()) { 444 myLastRequest = 445 invocation.asHttpRequest(getServerBase(), createExtraParams(null), getEncoding(), isPrettyPrint()); 446 } 447 448 OutcomeResponseHandler binding = new OutcomeResponseHandler(); 449 MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); 450 return resp; 451 } 452 453 @Override 454 public <T extends IBaseResource> T vread(final Class<T> theType, IdDt theId) { 455 if (!theId.hasVersionIdPart()) { 456 throw new IllegalArgumentException(Msg.code(1367) 457 + myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue())); 458 } 459 return doReadOrVRead(theType, theId, true, null, null, false, null, null, null, null, null); 460 } 461 462 @Override 463 public <T extends IBaseResource> T vread(Class<T> theType, String theId, String theVersionId) { 464 IdDt resId = new IdDt(toResourceName(theType), theId, theVersionId); 465 return vread(theType, resId); 466 } 467 468 private enum MetaOperation { 469 ADD, 470 DELETE, 471 GET 472 } 473 474 private abstract class BaseClientExecutable<T extends IClientExecutable<?, Y>, Y> 475 implements IClientExecutable<T, Y> { 476 477 EncodingEnum myParamEncoding; 478 Boolean myPrettyPrint; 479 SummaryEnum mySummaryMode; 480 CacheControlDirective myCacheControlDirective; 481 Map<String, List<String>> myCustomHeaderValues = new HashMap<>(); 482 private String myCustomAcceptHeaderValue; 483 private List<Class<? extends IBaseResource>> myPreferResponseTypes; 484 private boolean myQueryLogRequestAndResponse; 485 private Set<String> mySubsetElements; 486 487 public String getCustomAcceptHeaderValue() { 488 return myCustomAcceptHeaderValue; 489 } 490 491 @SuppressWarnings("unchecked") 492 @Override 493 public T accept(String theHeaderValue) { 494 myCustomAcceptHeaderValue = theHeaderValue; 495 return (T) this; 496 } 497 498 @Deprecated // override deprecated method 499 @SuppressWarnings("unchecked") 500 @Override 501 public T andLogRequestAndResponse(boolean theLogRequestAndResponse) { 502 myQueryLogRequestAndResponse = theLogRequestAndResponse; 503 return (T) this; 504 } 505 506 @SuppressWarnings("unchecked") 507 @Override 508 public T cacheControl(CacheControlDirective theCacheControlDirective) { 509 myCacheControlDirective = theCacheControlDirective; 510 return (T) this; 511 } 512 513 @SuppressWarnings("unchecked") 514 @Override 515 public T elementsSubset(String... theElements) { 516 if (theElements != null && theElements.length > 0) { 517 mySubsetElements = new HashSet<>(Arrays.asList(theElements)); 518 } else { 519 mySubsetElements = null; 520 } 521 return (T) this; 522 } 523 524 @SuppressWarnings("unchecked") 525 @Override 526 public T encoded(EncodingEnum theEncoding) { 527 Validate.notNull(theEncoding, "theEncoding must not be null"); 528 myParamEncoding = theEncoding; 529 return (T) this; 530 } 531 532 @SuppressWarnings("unchecked") 533 @Override 534 public T encodedJson() { 535 myParamEncoding = EncodingEnum.JSON; 536 return (T) this; 537 } 538 539 @SuppressWarnings("unchecked") 540 @Override 541 public T encodedXml() { 542 myParamEncoding = EncodingEnum.XML; 543 return (T) this; 544 } 545 546 @SuppressWarnings("unchecked") 547 @Override 548 public T withAdditionalHeader(String theHeaderName, String theHeaderValue) { 549 Objects.requireNonNull(theHeaderName, "headerName cannot be null"); 550 Objects.requireNonNull(theHeaderValue, "headerValue cannot be null"); 551 if (!myCustomHeaderValues.containsKey(theHeaderName)) { 552 myCustomHeaderValues.put(theHeaderName, new ArrayList<>()); 553 } 554 myCustomHeaderValues.get(theHeaderName).add(theHeaderValue); 555 return (T) this; 556 } 557 558 protected EncodingEnum getParamEncoding() { 559 return myParamEncoding; 560 } 561 562 public List<Class<? extends IBaseResource>> getPreferResponseTypes() { 563 return myPreferResponseTypes; 564 } 565 566 public List<Class<? extends IBaseResource>> getPreferResponseTypes(Class<? extends IBaseResource> theDefault) { 567 if (myPreferResponseTypes != null) { 568 return myPreferResponseTypes; 569 } 570 return toTypeList(theDefault); 571 } 572 573 protected Set<String> getSubsetElements() { 574 return mySubsetElements; 575 } 576 577 protected <Z> Z invoke( 578 Map<String, List<String>> theParams, 579 IClientResponseHandler<Z> theHandler, 580 BaseHttpClientInvocation theInvocation) { 581 if (isKeepResponses()) { 582 myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint); 583 } 584 585 Z resp = invokeClient( 586 myContext, 587 theHandler, 588 theInvocation, 589 myParamEncoding, 590 myPrettyPrint, 591 myQueryLogRequestAndResponse || myLogRequestAndResponse, 592 mySummaryMode, 593 mySubsetElements, 594 myCacheControlDirective, 595 myCustomAcceptHeaderValue, 596 myCustomHeaderValues); 597 return resp; 598 } 599 600 protected IBaseResource parseResourceBody(String theResourceBody) { 601 EncodingEnum encoding = EncodingEnum.detectEncodingNoDefault(theResourceBody); 602 if (encoding == null) { 603 throw new IllegalArgumentException(Msg.code(1368) 604 + myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); 605 } 606 return encoding.newParser(myContext).parseResource(theResourceBody); 607 } 608 609 @SuppressWarnings("unchecked") 610 @Override 611 public T preferResponseType(Class<? extends IBaseResource> theClass) { 612 myPreferResponseTypes = null; 613 if (theClass != null) { 614 myPreferResponseTypes = new ArrayList<>(); 615 myPreferResponseTypes.add(theClass); 616 } 617 return (T) this; 618 } 619 620 @SuppressWarnings("unchecked") 621 @Override 622 public T preferResponseTypes(List<Class<? extends IBaseResource>> theClass) { 623 myPreferResponseTypes = theClass; 624 return (T) this; 625 } 626 627 @SuppressWarnings("unchecked") 628 @Override 629 public T prettyPrint() { 630 myPrettyPrint = true; 631 return (T) this; 632 } 633 634 @SuppressWarnings("unchecked") 635 @Override 636 public T summaryMode(SummaryEnum theSummary) { 637 mySummaryMode = theSummary; 638 return ((T) this); 639 } 640 } 641 642 private abstract class BaseSearch< 643 EXEC extends IClientExecutable<?, OUTPUT>, QUERY extends IBaseQuery<QUERY>, OUTPUT> 644 extends BaseClientExecutable<EXEC, OUTPUT> implements IBaseQuery<QUERY> { 645 646 private Map<String, List<String>> myParams = new LinkedHashMap<>(); 647 648 @Override 649 public QUERY and(ICriterion<?> theCriterion) { 650 return where(theCriterion); 651 } 652 653 public Map<String, List<String>> getParamMap() { 654 return myParams; 655 } 656 657 @SuppressWarnings("unchecked") 658 @Override 659 public QUERY where(ICriterion<?> theCriterion) { 660 ICriterionInternal criterion = (ICriterionInternal) theCriterion; 661 662 String parameterName = criterion.getParameterName(); 663 String parameterValue = criterion.getParameterValue(myContext); 664 if (isNotBlank(parameterValue)) { 665 addParam(myParams, parameterName, parameterValue); 666 } 667 668 return (QUERY) this; 669 } 670 671 @Override 672 public QUERY whereMap(Map<String, List<String>> theRawMap) { 673 if (theRawMap != null) { 674 for (String nextKey : theRawMap.keySet()) { 675 for (String nextValue : theRawMap.get(nextKey)) { 676 addParam(myParams, nextKey, nextValue); 677 } 678 } 679 } 680 681 return (QUERY) this; 682 } 683 684 @SuppressWarnings("unchecked") 685 @Override 686 public QUERY where(Map<String, List<IQueryParameterType>> theCriterion) { 687 Validate.notNull(theCriterion, "theCriterion must not be null"); 688 for (Entry<String, List<IQueryParameterType>> nextEntry : theCriterion.entrySet()) { 689 String nextKey = nextEntry.getKey(); 690 List<IQueryParameterType> nextValues = nextEntry.getValue(); 691 for (IQueryParameterType nextValue : nextValues) { 692 String value = nextValue.getValueAsQueryToken(myContext); 693 String qualifier = nextValue.getQueryParameterQualifier(); 694 if (isNotBlank(qualifier)) { 695 nextKey = nextKey + qualifier; 696 } 697 addParam(myParams, nextKey, value); 698 } 699 } 700 return (QUERY) this; 701 } 702 } 703 704 private class CreateInternal extends BaseSearch<ICreateTyped, ICreateWithQueryTyped, MethodOutcome> 705 implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped { 706 707 private boolean myConditional; 708 private PreferReturnEnum myPrefer; 709 private IBaseResource myResource; 710 private String myResourceBody; 711 private String mySearchUrl; 712 713 @Override 714 public ICreateWithQuery conditional() { 715 myConditional = true; 716 return this; 717 } 718 719 @Override 720 public ICreateTyped conditionalByUrl(String theSearchUrl) { 721 mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); 722 return this; 723 } 724 725 @Override 726 public MethodOutcome execute() { 727 if (myResource == null) { 728 myResource = parseResourceBody(myResourceBody); 729 } 730 731 // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding 732 if (getParamEncoding() != null) { 733 myResourceBody = null; 734 } 735 736 BaseHttpClientInvocation invocation; 737 if (mySearchUrl != null) { 738 invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myContext, mySearchUrl); 739 } else if (myConditional) { 740 invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myContext, getParamMap()); 741 } else { 742 invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myContext); 743 } 744 745 addPreferHeader(myPrefer, invocation); 746 747 OutcomeResponseHandler binding = new OutcomeResponseHandler(myPrefer); 748 749 Map<String, List<String>> params = new HashMap<>(); 750 return invoke(params, binding, invocation); 751 } 752 753 @Override 754 public ICreateTyped prefer(PreferReturnEnum theReturn) { 755 myPrefer = theReturn; 756 return this; 757 } 758 759 @Override 760 public ICreateTyped resource(IBaseResource theResource) { 761 Validate.notNull(theResource, "Resource can not be null"); 762 myResource = theResource; 763 return this; 764 } 765 766 @Override 767 public ICreateTyped resource(String theResourceBody) { 768 Validate.notBlank(theResourceBody, "Body can not be null or blank"); 769 myResourceBody = theResourceBody; 770 return this; 771 } 772 } 773 774 private class DeleteInternal extends BaseSearch<IDeleteTyped, IDeleteWithQueryTyped, MethodOutcome> 775 implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped { 776 777 private boolean myConditional; 778 private IIdType myId; 779 private String myResourceType; 780 private String mySearchUrl; 781 private DeleteCascadeModeEnum myCascadeMode; 782 783 @Override 784 public MethodOutcome execute() { 785 786 Map<String, List<String>> additionalParams = new HashMap<>(); 787 if (myCascadeMode != null) { 788 switch (myCascadeMode) { 789 case DELETE: 790 addParam(getParamMap(), Constants.PARAMETER_CASCADE_DELETE, Constants.CASCADE_DELETE); 791 break; 792 default: 793 case NONE: 794 break; 795 } 796 } 797 798 HttpDeleteClientInvocation invocation; 799 if (myId != null) { 800 invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myId, getParamMap()); 801 } else if (myConditional) { 802 invocation = 803 DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myResourceType, getParamMap()); 804 } else { 805 invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), mySearchUrl, getParamMap()); 806 } 807 808 OutcomeResponseHandler binding = new OutcomeResponseHandler(); 809 810 return invoke(additionalParams, binding, invocation); 811 } 812 813 @Override 814 public IDeleteTyped resource(IBaseResource theResource) { 815 Validate.notNull(theResource, "theResource can not be null"); 816 IIdType id = theResource.getIdElement(); 817 Validate.notNull(id, "theResource.getIdElement() can not be null"); 818 if (!id.hasResourceType() || !id.hasIdPart()) { 819 throw new IllegalArgumentException(Msg.code(1369) 820 + "theResource.getId() must contain a resource type and logical ID at a minimum (e.g. Patient/1234), found: " 821 + id.getValue()); 822 } 823 myId = id; 824 return this; 825 } 826 827 @Override 828 public IDeleteTyped resourceById(IIdType theId) { 829 Validate.notNull(theId, "theId can not be null"); 830 if (!theId.hasResourceType() || !theId.hasIdPart()) { 831 throw new IllegalArgumentException(Msg.code(1370) 832 + "theId must contain a resource type and logical ID at a minimum (e.g. Patient/1234)found: " 833 + theId.getValue()); 834 } 835 myId = theId; 836 return this; 837 } 838 839 @Override 840 public IDeleteTyped resourceById(String theResourceType, String theLogicalId) { 841 Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); 842 if (myContext.getResourceDefinition(theResourceType) == null) { 843 throw new IllegalArgumentException(Msg.code(1371) + "Unknown resource type"); 844 } 845 Validate.notBlank(theLogicalId, "theLogicalId can not be blank/null"); 846 if (theLogicalId.contains("/")) { 847 throw new IllegalArgumentException(Msg.code(1372) 848 + "LogicalId can not contain '/' (should only be the logical ID portion, not a qualified ID)"); 849 } 850 myId = new IdDt(theResourceType, theLogicalId); 851 return this; 852 } 853 854 @Override 855 public IDeleteWithQuery resourceConditionalByType(Class<? extends IBaseResource> theResourceType) { 856 Validate.notNull(theResourceType, "theResourceType can not be null"); 857 myConditional = true; 858 myResourceType = myContext.getResourceType(theResourceType); 859 return this; 860 } 861 862 @Override 863 public IDeleteWithQuery resourceConditionalByType(String theResourceType) { 864 Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); 865 if (myContext.getResourceDefinition(theResourceType) == null) { 866 throw new IllegalArgumentException(Msg.code(1373) + "Unknown resource type: " + theResourceType); 867 } 868 myResourceType = theResourceType; 869 myConditional = true; 870 return this; 871 } 872 873 @Override 874 public IDeleteTyped resourceConditionalByUrl(String theSearchUrl) { 875 mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); 876 return this; 877 } 878 879 @Override 880 public IDeleteTyped cascade(DeleteCascadeModeEnum theDelete) { 881 myCascadeMode = theDelete; 882 return this; 883 } 884 } 885 886 @SuppressWarnings({"rawtypes", "unchecked"}) 887 private class FetchConformanceInternal extends BaseClientExecutable 888 implements IFetchConformanceUntyped, IFetchConformanceTyped { 889 private RuntimeResourceDefinition myType; 890 891 @Override 892 public Object execute() { 893 ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass()); 894 FhirContext fhirContext = getFhirContext(); 895 HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(fhirContext); 896 return super.invoke(null, binding, invocation); 897 } 898 899 @Override 900 public <T extends IBaseConformance> IFetchConformanceTyped<T> ofType(Class<T> theResourceType) { 901 Validate.notNull(theResourceType, "theResourceType must not be null"); 902 myType = myContext.getResourceDefinition(theResourceType); 903 if (myType == null) { 904 throw new IllegalArgumentException(Msg.code(1374) 905 + myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); 906 } 907 return this; 908 } 909 } 910 911 @SuppressWarnings({"unchecked", "rawtypes"}) 912 private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object> 913 implements IGetPageTyped<Object> { 914 915 private Class<? extends IBaseBundle> myBundleType; 916 private String myUrl; 917 918 private PagingHttpMethodEnum myPagingHttpMethod = PagingHttpMethodEnum.GET; 919 920 public GetPageInternal(String theUrl, Class<? extends IBaseBundle> theBundleType) { 921 myUrl = theUrl; 922 myBundleType = theBundleType; 923 } 924 925 @Override 926 public Object execute() { 927 IClientResponseHandler binding = new ResourceResponseHandler(myBundleType, getPreferResponseTypes()); 928 HttpSimpleClientInvocation invocationGet = 929 new HttpSimpleClientInvocation(myContext, myUrl, myPagingHttpMethod); 930 return invoke(null, binding, invocationGet); 931 } 932 933 @Override 934 public IGetPageTyped<Object> usingMethod(PagingHttpMethodEnum thePagingHttpMethod) { 935 myPagingHttpMethod = thePagingHttpMethod; 936 return this; 937 } 938 } 939 940 @SuppressWarnings("rawtypes") 941 private class HistoryInternal extends BaseClientExecutable implements IHistory, IHistoryUntyped, IHistoryTyped { 942 943 private Integer myCount; 944 private IIdType myId; 945 private Class<? extends IBaseBundle> myReturnType; 946 private IPrimitiveType mySince; 947 private Class<? extends IBaseResource> myType; 948 private DateRangeParam myAt; 949 950 @SuppressWarnings("unchecked") 951 @Override 952 public IHistoryTyped andReturnBundle(Class theType) { 953 return returnBundle(theType); 954 } 955 956 @Override 957 public IHistoryTyped returnBundle(Class theType) { 958 Validate.notNull(theType, "theType must not be null on method andReturnBundle(Class)"); 959 myReturnType = theType; 960 return this; 961 } 962 963 @Override 964 public IHistoryTyped at(DateRangeParam theDateRangeParam) { 965 myAt = theDateRangeParam; 966 return this; 967 } 968 969 @Override 970 public IHistoryTyped count(Integer theCount) { 971 myCount = theCount; 972 return this; 973 } 974 975 @SuppressWarnings("unchecked") 976 @Override 977 public Object execute() { 978 String resourceName; 979 String id; 980 if (myType != null) { 981 resourceName = myContext.getResourceType(myType); 982 id = null; 983 } else if (myId != null) { 984 resourceName = myId.getResourceType(); 985 id = myId.getIdPart(); 986 } else { 987 resourceName = null; 988 id = null; 989 } 990 991 HttpGetClientInvocation invocation = 992 HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, mySince, myCount, myAt); 993 994 IClientResponseHandler handler; 995 handler = new ResourceResponseHandler(myReturnType, getPreferResponseTypes(myType)); 996 997 return invoke(null, handler, invocation); 998 } 999 1000 @Override 1001 public IHistoryUntyped onInstance(IIdType theId) { 1002 if (!theId.hasResourceType()) { 1003 throw new IllegalArgumentException( 1004 Msg.code(1375) + "Resource ID does not have a resource type: " + theId.getValue()); 1005 } 1006 myId = theId; 1007 return this; 1008 } 1009 1010 @Override 1011 public IHistoryUntyped onInstance(String theId) { 1012 Validate.notBlank(theId, "theId must not be null or blank"); 1013 IIdType id = myContext.getVersion().newIdType(); 1014 id.setValue(theId); 1015 return onInstance(id); 1016 } 1017 1018 @Override 1019 public IHistoryUntyped onServer() { 1020 return this; 1021 } 1022 1023 @Override 1024 public IHistoryUntyped onType(Class<? extends IBaseResource> theResourceType) { 1025 myType = theResourceType; 1026 return this; 1027 } 1028 1029 @Override 1030 public IHistoryUntyped onType(String theResourceType) { 1031 myType = myContext.getResourceDefinition(theResourceType).getImplementingClass(); 1032 return this; 1033 } 1034 1035 @Override 1036 public IHistoryTyped since(Date theCutoff) { 1037 if (theCutoff != null) { 1038 mySince = new InstantDt(theCutoff); 1039 } else { 1040 mySince = null; 1041 } 1042 return this; 1043 } 1044 1045 @Override 1046 public IHistoryTyped since(IPrimitiveType theCutoff) { 1047 mySince = theCutoff; 1048 return this; 1049 } 1050 } 1051 1052 @SuppressWarnings({"unchecked", "rawtypes"}) 1053 private final class LoadPageInternal implements IGetPage, IGetPageUntyped { 1054 1055 private static final String PREV = "prev"; 1056 private static final String PREVIOUS = "previous"; 1057 private String myPageUrl; 1058 1059 @Override 1060 public <T extends IBaseBundle> IGetPageTyped andReturnBundle(Class<T> theBundleType) { 1061 Validate.notNull(theBundleType, "theBundleType must not be null"); 1062 return new GetPageInternal(myPageUrl, theBundleType); 1063 } 1064 1065 @Override 1066 public IGetPageUntyped byUrl(String thePageUrl) { 1067 if (isBlank(thePageUrl)) { 1068 throw new IllegalArgumentException(Msg.code(1376) + "thePagingUrl must not be blank or null"); 1069 } 1070 myPageUrl = thePageUrl; 1071 return this; 1072 } 1073 1074 @Override 1075 public <T extends IBaseBundle> IGetPageTyped<T> next(T theBundle) { 1076 return nextOrPrevious("next", theBundle); 1077 } 1078 1079 private <T extends IBaseBundle> IGetPageTyped<T> nextOrPrevious(String theWantRel, T theBundle) { 1080 RuntimeResourceDefinition def = myContext.getResourceDefinition(theBundle); 1081 List<IBase> links = def.getChildByName("link").getAccessor().getValues(theBundle); 1082 if (links == null || links.isEmpty()) { 1083 throw new IllegalArgumentException(Msg.code(1377) 1084 + myContext 1085 .getLocalizer() 1086 .getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); 1087 } 1088 for (IBase nextLink : links) { 1089 BaseRuntimeElementCompositeDefinition linkDef = 1090 (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(nextLink.getClass()); 1091 List<IBase> rel = 1092 linkDef.getChildByName("relation").getAccessor().getValues(nextLink); 1093 if (rel == null || rel.isEmpty()) { 1094 continue; 1095 } 1096 String relation = ((IPrimitiveType<?>) rel.get(0)).getValueAsString(); 1097 if (theWantRel.equals(relation) || (PREVIOUS.equals(theWantRel) && PREV.equals(relation))) { 1098 List<IBase> urls = 1099 linkDef.getChildByName("url").getAccessor().getValues(nextLink); 1100 if (urls == null || urls.isEmpty()) { 1101 continue; 1102 } 1103 String url = ((IPrimitiveType<?>) urls.get(0)).getValueAsString(); 1104 if (isBlank(url)) { 1105 continue; 1106 } 1107 return (IGetPageTyped<T>) byUrl(url).andReturnBundle(theBundle.getClass()); 1108 } 1109 } 1110 throw new IllegalArgumentException(Msg.code(1378) 1111 + myContext 1112 .getLocalizer() 1113 .getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); 1114 } 1115 1116 @Override 1117 public <T extends IBaseBundle> IGetPageTyped<T> previous(T theBundle) { 1118 return nextOrPrevious(PREVIOUS, theBundle); 1119 } 1120 } 1121 1122 @SuppressWarnings("rawtypes") 1123 private class MetaInternal extends BaseClientExecutable 1124 implements IMeta, IMetaAddOrDeleteUnsourced, IMetaGetUnsourced, IMetaAddOrDeleteSourced { 1125 1126 private IIdType myId; 1127 private IBaseMetaType myMeta; 1128 private Class<? extends IBaseMetaType> myMetaType; 1129 private String myOnType; 1130 private MetaOperation myOperation; 1131 1132 @Override 1133 public IMetaAddOrDeleteUnsourced add() { 1134 myOperation = MetaOperation.ADD; 1135 return this; 1136 } 1137 1138 @Override 1139 public IMetaAddOrDeleteUnsourced delete() { 1140 myOperation = MetaOperation.DELETE; 1141 return this; 1142 } 1143 1144 @SuppressWarnings("unchecked") 1145 @Override 1146 public Object execute() { 1147 1148 BaseHttpClientInvocation invocation = null; 1149 1150 IBaseParameters parameters = ParametersUtil.newInstance(myContext); 1151 switch (myOperation) { 1152 case ADD: 1153 ParametersUtil.addParameterToParameters(myContext, parameters, "meta", myMeta); 1154 invocation = OperationMethodBinding.createOperationInvocation( 1155 myContext, myId.getResourceType(), myId.getIdPart(), null, "$meta-add", parameters, false); 1156 break; 1157 case DELETE: 1158 ParametersUtil.addParameterToParameters(myContext, parameters, "meta", myMeta); 1159 invocation = OperationMethodBinding.createOperationInvocation( 1160 myContext, 1161 myId.getResourceType(), 1162 myId.getIdPart(), 1163 null, 1164 "$meta-delete", 1165 parameters, 1166 false); 1167 break; 1168 case GET: 1169 if (myId != null) { 1170 invocation = OperationMethodBinding.createOperationInvocation( 1171 myContext, myOnType, myId.getIdPart(), null, "$meta", parameters, true); 1172 } else if (myOnType != null) { 1173 invocation = OperationMethodBinding.createOperationInvocation( 1174 myContext, myOnType, null, null, "$meta", parameters, true); 1175 } else { 1176 invocation = OperationMethodBinding.createOperationInvocation( 1177 myContext, null, null, null, "$meta", parameters, true); 1178 } 1179 break; 1180 } 1181 1182 // Should not happen 1183 if (invocation == null) { 1184 throw new IllegalStateException(Msg.code(1379)); 1185 } 1186 1187 IClientResponseHandler handler; 1188 handler = new MetaParametersResponseHandler(myMetaType); 1189 return invoke(null, handler, invocation); 1190 } 1191 1192 @Override 1193 public IClientExecutable fromResource(IIdType theId) { 1194 setIdInternal(theId); 1195 return this; 1196 } 1197 1198 @Override 1199 public IClientExecutable fromServer() { 1200 return this; 1201 } 1202 1203 @Override 1204 public IClientExecutable fromType(String theResourceName) { 1205 Validate.notBlank(theResourceName, "theResourceName must not be blank"); 1206 myOnType = theResourceName; 1207 return this; 1208 } 1209 1210 @SuppressWarnings("unchecked") 1211 @Override 1212 public <T extends IBaseMetaType> IMetaGetUnsourced<T> get(Class<T> theType) { 1213 myMetaType = theType; 1214 myOperation = MetaOperation.GET; 1215 return this; 1216 } 1217 1218 @SuppressWarnings("unchecked") 1219 @Override 1220 public <T extends IBaseMetaType> IClientExecutable<IClientExecutable<?, T>, T> meta(T theMeta) { 1221 Validate.notNull(theMeta, "theMeta must not be null"); 1222 myMeta = theMeta; 1223 myMetaType = myMeta.getClass(); 1224 return this; 1225 } 1226 1227 @Override 1228 public IMetaAddOrDeleteSourced onResource(IIdType theId) { 1229 setIdInternal(theId); 1230 return this; 1231 } 1232 1233 private void setIdInternal(IIdType theId) { 1234 Validate.notBlank(theId.getResourceType(), "theId must contain a resource type"); 1235 Validate.notBlank(theId.getIdPart(), "theId must contain an ID part"); 1236 myOnType = theId.getResourceType(); 1237 myId = theId; 1238 } 1239 } 1240 1241 private final class MetaParametersResponseHandler<T extends IBaseMetaType> implements IClientResponseHandler<T> { 1242 1243 private Class<T> myType; 1244 1245 public MetaParametersResponseHandler(Class<T> theMetaType) { 1246 myType = theMetaType; 1247 } 1248 1249 @SuppressWarnings("unchecked") 1250 @Override 1251 public T invokeClient( 1252 String theResponseMimeType, 1253 InputStream theResponseInputStream, 1254 int theResponseStatusCode, 1255 Map<String, List<String>> theHeaders) 1256 throws BaseServerResponseException { 1257 EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); 1258 if (respType == null) { 1259 throw NonFhirResponseException.newInstance( 1260 theResponseStatusCode, theResponseMimeType, theResponseInputStream); 1261 } 1262 IParser parser = respType.newParser(myContext); 1263 RuntimeResourceDefinition type = myContext.getResourceDefinition("Parameters"); 1264 IBaseResource retVal = parser.parseResource(type.getImplementingClass(), theResponseInputStream); 1265 1266 BaseRuntimeChildDefinition paramChild = type.getChildByName("parameter"); 1267 BaseRuntimeElementCompositeDefinition<?> paramChildElem = 1268 (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter"); 1269 List<IBase> parameter = paramChild.getAccessor().getValues(retVal); 1270 if (parameter == null || parameter.isEmpty()) { 1271 return (T) myContext.getElementDefinition(myType).newInstance(); 1272 } 1273 IBase param = parameter.get(0); 1274 1275 List<IBase> meta = 1276 paramChildElem.getChildByName("value[x]").getAccessor().getValues(param); 1277 if (meta.isEmpty()) { 1278 return (T) myContext.getElementDefinition(myType).newInstance(); 1279 } 1280 return (T) meta.get(0); 1281 } 1282 } 1283 1284 @SuppressWarnings("rawtypes") 1285 private class OperationInternal extends BaseClientExecutable 1286 implements IOperation, 1287 IOperationUnnamed, 1288 IOperationUntyped, 1289 IOperationUntypedWithInput, 1290 IOperationUntypedWithInputAndPartialOutput, 1291 IOperationProcessMsg, 1292 IOperationProcessMsgMode { 1293 1294 private IIdType myId; 1295 private Boolean myIsAsync; 1296 private IBaseBundle myMsgBundle; 1297 private String myOperationName; 1298 private IBaseParameters myParameters; 1299 private RuntimeResourceDefinition myParametersDef; 1300 private String myResponseUrl; 1301 private Class myReturnResourceType; 1302 private Class<? extends IBaseResource> myType; 1303 private boolean myUseHttpGet; 1304 private boolean myReturnMethodOutcome; 1305 1306 @SuppressWarnings("unchecked") 1307 private void addParam(String theName, IBase theValue) { 1308 BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter"); 1309 BaseRuntimeElementCompositeDefinition<?> parameterElem = 1310 (BaseRuntimeElementCompositeDefinition<?>) parameterChild.getChildByName("parameter"); 1311 1312 IBase parameter = parameterElem.newInstance(); 1313 parameterChild.getMutator().addValue(myParameters, parameter); 1314 1315 IPrimitiveType<String> name = (IPrimitiveType<String>) 1316 myContext.getElementDefinition("string").newInstance(); 1317 name.setValue(theName); 1318 parameterElem.getChildByName("name").getMutator().setValue(parameter, name); 1319 1320 if (theValue instanceof IBaseDatatype) { 1321 BaseRuntimeElementDefinition<?> datatypeDef = myContext.getElementDefinition(theValue.getClass()); 1322 if (datatypeDef instanceof IRuntimeDatatypeDefinition) { 1323 Class<? extends IBaseDatatype> profileOf = 1324 ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf(); 1325 if (profileOf != null) { 1326 datatypeDef = myContext.getElementDefinition(profileOf); 1327 } 1328 } 1329 String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName()); 1330 BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName); 1331 childByName.getMutator().setValue(parameter, theValue); 1332 } else if (theValue instanceof IBaseResource) { 1333 parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue); 1334 } else { 1335 throw new IllegalArgumentException( 1336 Msg.code(1380) + "Don't know how to handle parameter of type " + theValue.getClass()); 1337 } 1338 } 1339 1340 private void addParam(String theName, IQueryParameterType theValue) { 1341 IPrimitiveType<?> stringType = 1342 ParametersUtil.createString(myContext, theValue.getValueAsQueryToken(myContext)); 1343 addParam(theName, stringType); 1344 } 1345 1346 @Override 1347 public IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue) { 1348 Validate.notEmpty(theName, "theName must not be null"); 1349 Validate.notNull(theValue, "theValue must not be null"); 1350 addParam(theName, theValue); 1351 return this; 1352 } 1353 1354 @Override 1355 public IOperationUntypedWithInputAndPartialOutput andSearchParameter( 1356 String theName, IQueryParameterType theValue) { 1357 addParam(theName, theValue); 1358 1359 return this; 1360 } 1361 1362 @Override 1363 public IOperationProcessMsgMode asynchronous(Class theResponseClass) { 1364 myIsAsync = true; 1365 Validate.notNull(theResponseClass, "theReturnType must not be null"); 1366 Validate.isTrue( 1367 IBaseResource.class.isAssignableFrom(theResponseClass), 1368 "theReturnType must be a class which extends from IBaseResource"); 1369 myReturnResourceType = theResponseClass; 1370 return this; 1371 } 1372 1373 @SuppressWarnings("unchecked") 1374 @Override 1375 public Object execute() { 1376 if (myOperationName != null 1377 && myOperationName.equals(Constants.EXTOP_PROCESS_MESSAGE) 1378 && myMsgBundle != null) { 1379 Map<String, List<String>> urlParams = new LinkedHashMap<String, List<String>>(); 1380 // Set Url parameter Async and Response-Url 1381 if (myIsAsync != null) { 1382 urlParams.put(Constants.PARAM_ASYNC, Arrays.asList(String.valueOf(myIsAsync))); 1383 } 1384 1385 if (myResponseUrl != null && isNotBlank(myResponseUrl)) { 1386 urlParams.put(Constants.PARAM_RESPONSE_URL, Arrays.asList(String.valueOf(myResponseUrl))); 1387 } 1388 // If is $process-message operation 1389 BaseHttpClientInvocation invocation = OperationMethodBinding.createProcessMsgInvocation( 1390 myContext, myOperationName, myMsgBundle, urlParams); 1391 1392 ResourceResponseHandler handler = new ResourceResponseHandler(); 1393 handler.setPreferResponseTypes(getPreferResponseTypes(myType)); 1394 1395 Object retVal = invoke(null, handler, invocation); 1396 return retVal; 1397 } 1398 1399 String resourceName; 1400 String id; 1401 String version; 1402 if (myType != null) { 1403 resourceName = myContext.getResourceType(myType); 1404 id = null; 1405 version = null; 1406 } else if (myId != null) { 1407 resourceName = myId.getResourceType(); 1408 Validate.notBlank( 1409 defaultString(resourceName), 1410 "Can not invoke operation \"$%s\" on instance \"%s\" - No resource type specified", 1411 myOperationName, 1412 myId.getValue()); 1413 id = myId.getIdPart(); 1414 version = myId.getVersionIdPart(); 1415 } else { 1416 resourceName = null; 1417 id = null; 1418 version = null; 1419 } 1420 1421 BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation( 1422 myContext, resourceName, id, version, myOperationName, myParameters, myUseHttpGet); 1423 1424 if (myReturnResourceType != null) { 1425 ResourceResponseHandler handler; 1426 if (IBaseBinary.class.isAssignableFrom(myReturnResourceType)) { 1427 handler = new ResourceOrBinaryResponseHandler(); 1428 } else { 1429 handler = new ResourceResponseHandler(myReturnResourceType); 1430 } 1431 Object retVal = invoke(null, handler, invocation); 1432 return retVal; 1433 } 1434 IClientResponseHandler handler = 1435 new ResourceOrBinaryResponseHandler().setPreferResponseTypes(getPreferResponseTypes(myType)); 1436 1437 if (myReturnMethodOutcome) { 1438 handler = new MethodOutcomeResponseHandler(handler); 1439 } 1440 1441 Object retVal = invoke(null, handler, invocation); 1442 1443 if (myReturnMethodOutcome) { 1444 return retVal; 1445 } 1446 1447 if (myContext 1448 .getResourceDefinition((IBaseResource) retVal) 1449 .getName() 1450 .equals("Parameters")) { 1451 return retVal; 1452 } 1453 RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); 1454 IBaseResource parameters = def.newInstance(); 1455 1456 BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); 1457 BaseRuntimeElementCompositeDefinition<?> paramChildElem = 1458 (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter"); 1459 IBase parameter = paramChildElem.newInstance(); 1460 paramChild.getMutator().addValue(parameters, parameter); 1461 1462 BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); 1463 resourceElem.getMutator().addValue(parameter, (IBase) retVal); 1464 1465 return parameters; 1466 } 1467 1468 @Override 1469 public IOperationUntyped named(String theName) { 1470 Validate.notBlank(theName, "theName can not be null"); 1471 myOperationName = theName; 1472 return this; 1473 } 1474 1475 @Override 1476 public IOperationUnnamed onInstance(IIdType theId) { 1477 myId = theId.toVersionless(); 1478 return this; 1479 } 1480 1481 @Override 1482 public IOperationUnnamed onInstance(String theId) { 1483 Validate.notBlank(theId, "theId must not be null or blank"); 1484 IIdType id = myContext.getVersion().newIdType(); 1485 id.setValue(theId); 1486 return onInstance(id); 1487 } 1488 1489 @Override 1490 public IOperationUnnamed onInstanceVersion(IIdType theId) { 1491 myId = theId; 1492 return this; 1493 } 1494 1495 @Override 1496 public IOperationUnnamed onServer() { 1497 return this; 1498 } 1499 1500 @Override 1501 public IOperationUnnamed onType(Class<? extends IBaseResource> theResourceType) { 1502 myType = theResourceType; 1503 return this; 1504 } 1505 1506 @Override 1507 public IOperationUnnamed onType(String theResourceType) { 1508 myType = myContext.getResourceDefinition(theResourceType).getImplementingClass(); 1509 return this; 1510 } 1511 1512 @Override 1513 public IOperationProcessMsg processMessage() { 1514 myOperationName = Constants.EXTOP_PROCESS_MESSAGE; 1515 return this; 1516 } 1517 1518 @Override 1519 public IOperationUntypedWithInput returnResourceType(Class theReturnType) { 1520 Validate.notNull(theReturnType, "theReturnType must not be null"); 1521 Validate.isTrue( 1522 IBaseResource.class.isAssignableFrom(theReturnType), 1523 "theReturnType must be a class which extends from IBaseResource"); 1524 myReturnResourceType = theReturnType; 1525 return this; 1526 } 1527 1528 @Override 1529 public IOperationUntypedWithInput returnMethodOutcome() { 1530 myReturnMethodOutcome = true; 1531 return this; 1532 } 1533 1534 @SuppressWarnings("unchecked") 1535 @Override 1536 public IOperationProcessMsgMode setMessageBundle(IBaseBundle theMsgBundle) { 1537 1538 Validate.notNull(theMsgBundle, "theMsgBundle must not be null"); 1539 /* 1540 * Validate.isTrue(theMsgBundle.getType().getValueAsEnum() == BundleTypeEnum.MESSAGE); 1541 * Validate.isTrue(theMsgBundle.getEntries().size() > 0); 1542 * Validate.notNull(theMsgBundle.getEntries().get(0).getResource(), "Message Bundle first entry must be a MessageHeader resource"); 1543 * Validate.isTrue(theMsgBundle.getEntries().get(0).getResource().getResourceName().equals("MessageHeader"), "Message Bundle first entry must be a MessageHeader resource"); 1544 */ 1545 myMsgBundle = theMsgBundle; 1546 return this; 1547 } 1548 1549 @Override 1550 public IOperationProcessMsg setResponseUrlParam(String responseUrl) { 1551 Validate.notEmpty(responseUrl, "responseUrl must not be null"); 1552 Validate.matchesPattern( 1553 responseUrl, 1554 "^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", 1555 "responseUrl must be a valid URL"); 1556 myResponseUrl = responseUrl; 1557 return this; 1558 } 1559 1560 @Override 1561 public IOperationProcessMsgMode synchronous(Class theResponseClass) { 1562 myIsAsync = false; 1563 Validate.notNull(theResponseClass, "theReturnType must not be null"); 1564 Validate.isTrue( 1565 IBaseResource.class.isAssignableFrom(theResponseClass), 1566 "theReturnType must be a class which extends from IBaseResource"); 1567 myReturnResourceType = theResponseClass; 1568 return this; 1569 } 1570 1571 @Override 1572 public IOperationUntypedWithInput useHttpGet() { 1573 myUseHttpGet = true; 1574 return this; 1575 } 1576 1577 @SuppressWarnings("unchecked") 1578 @Override 1579 public <T extends IBaseParameters> IOperationUntypedWithInputAndPartialOutput<T> withNoParameters( 1580 Class<T> theOutputParameterType) { 1581 Validate.notNull(theOutputParameterType, "theOutputParameterType may not be null"); 1582 RuntimeResourceDefinition def = myContext.getResourceDefinition(theOutputParameterType); 1583 if (def == null) { 1584 throw new IllegalArgumentException( 1585 Msg.code(1381) + "theOutputParameterType must refer to a HAPI FHIR Resource type: " 1586 + theOutputParameterType.getName()); 1587 } 1588 if (!"Parameters".equals(def.getName())) { 1589 throw new IllegalArgumentException(Msg.code(1382) 1590 + "theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " 1591 + "Parameters" + " - " + theOutputParameterType.getName() + " is a resource named: " 1592 + def.getName()); 1593 } 1594 myParameters = (IBaseParameters) def.newInstance(); 1595 return this; 1596 } 1597 1598 @SuppressWarnings("unchecked") 1599 @Override 1600 public <T extends IBaseParameters> IOperationUntypedWithInputAndPartialOutput<T> withParameter( 1601 Class<T> theParameterType, String theName, IBase theValue) { 1602 Validate.notNull(theParameterType, "theParameterType must not be null"); 1603 Validate.notEmpty(theName, "theName must not be null"); 1604 Validate.notNull(theValue, "theValue must not be null"); 1605 1606 myParametersDef = myContext.getResourceDefinition(theParameterType); 1607 myParameters = (IBaseParameters) myParametersDef.newInstance(); 1608 1609 addParam(theName, theValue); 1610 1611 return this; 1612 } 1613 1614 @SuppressWarnings({"unchecked"}) 1615 @Override 1616 public IOperationUntypedWithInputAndPartialOutput withParameters(IBaseParameters theParameters) { 1617 Validate.notNull(theParameters, "theParameters can not be null"); 1618 myParameters = theParameters; 1619 myParametersDef = myContext.getResourceDefinition(theParameters.getClass()); 1620 return this; 1621 } 1622 1623 @SuppressWarnings("unchecked") 1624 @Override 1625 public <T extends IBaseParameters> IOperationUntypedWithInputAndPartialOutput<T> withSearchParameter( 1626 Class<T> theParameterType, String theName, IQueryParameterType theValue) { 1627 Validate.notNull(theParameterType, "theParameterType must not be null"); 1628 Validate.notEmpty(theName, "theName must not be null"); 1629 Validate.notNull(theValue, "theValue must not be null"); 1630 1631 myParametersDef = myContext.getResourceDefinition(theParameterType); 1632 myParameters = (IBaseParameters) myParametersDef.newInstance(); 1633 1634 addParam(theName, theValue); 1635 1636 return this; 1637 } 1638 } 1639 1640 private final class MethodOutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> { 1641 private final IClientResponseHandler<? extends IBaseResource> myWrap; 1642 1643 private MethodOutcomeResponseHandler(IClientResponseHandler<? extends IBaseResource> theWrap) { 1644 myWrap = theWrap; 1645 } 1646 1647 @Override 1648 public MethodOutcome invokeClient( 1649 String theResponseMimeType, 1650 InputStream theResponseInputStream, 1651 int theResponseStatusCode, 1652 Map<String, List<String>> theHeaders) 1653 throws IOException, BaseServerResponseException { 1654 IBaseResource response = 1655 myWrap.invokeClient(theResponseMimeType, theResponseInputStream, theResponseStatusCode, theHeaders); 1656 1657 MethodOutcome retVal = new MethodOutcome(); 1658 if (response instanceof IBaseOperationOutcome) { 1659 retVal.setOperationOutcome((IBaseOperationOutcome) response); 1660 } else { 1661 retVal.setResource(response); 1662 } 1663 retVal.setCreatedUsingStatusCode(theResponseStatusCode); 1664 retVal.setResponseStatusCode(theResponseStatusCode); 1665 retVal.setResponseHeaders(theHeaders); 1666 return retVal; 1667 } 1668 } 1669 1670 private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> { 1671 private PreferReturnEnum myPrefer; 1672 1673 private OutcomeResponseHandler() { 1674 super(); 1675 } 1676 1677 private OutcomeResponseHandler(PreferReturnEnum thePrefer) { 1678 this(); 1679 myPrefer = thePrefer; 1680 } 1681 1682 @Override 1683 public MethodOutcome invokeClient( 1684 String theResponseMimeType, 1685 InputStream theResponseInputStream, 1686 int theResponseStatusCode, 1687 Map<String, List<String>> theHeaders) 1688 throws BaseServerResponseException { 1689 MethodOutcome response = MethodUtil.process2xxResponse( 1690 myContext, theResponseStatusCode, theResponseMimeType, theResponseInputStream, theHeaders); 1691 response.setCreatedUsingStatusCode(theResponseStatusCode); 1692 1693 if (myPrefer == PreferReturnEnum.REPRESENTATION) { 1694 if (response.getResource() == null) { 1695 if (response.getId() != null 1696 && isNotBlank(response.getId().getValue()) 1697 && response.getId().hasBaseUrl()) { 1698 ourLog.info( 1699 "Server did not return resource for Prefer-representation, going to fetch: {}", 1700 response.getId().getValue()); 1701 IBaseResource resource = read().resource( 1702 response.getId().getResourceType()) 1703 .withUrl(response.getId()) 1704 .execute(); 1705 response.setResource(resource); 1706 } 1707 } 1708 } 1709 1710 response.setResponseHeaders(theHeaders); 1711 1712 return response; 1713 } 1714 } 1715 1716 private class PatchInternal extends BaseSearch<IPatchExecutable, IPatchWithQueryTyped, MethodOutcome> 1717 implements IPatch, IPatchWithBody, IPatchExecutable, IPatchWithQuery, IPatchWithQueryTyped { 1718 1719 private boolean myConditional; 1720 private IIdType myId; 1721 private String myPatchBody; 1722 private PatchTypeEnum myPatchType; 1723 private PreferReturnEnum myPrefer; 1724 private String myResourceType; 1725 private String mySearchUrl; 1726 1727 @Override 1728 public IPatchWithQuery conditional(Class<? extends IBaseResource> theClass) { 1729 Validate.notNull(theClass, "theClass must not be null"); 1730 String resourceType = myContext.getResourceType(theClass); 1731 return conditional(resourceType); 1732 } 1733 1734 @Override 1735 public IPatchWithQuery conditional(String theResourceType) { 1736 Validate.notBlank(theResourceType, "theResourceType must not be null"); 1737 myResourceType = theResourceType; 1738 myConditional = true; 1739 return this; 1740 } 1741 1742 // TODO: This is not longer used.. Deprecate it or just remove it? 1743 @Override 1744 public IPatchWithBody conditionalByUrl(String theSearchUrl) { 1745 mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); 1746 return this; 1747 } 1748 1749 @Override 1750 public MethodOutcome execute() { 1751 1752 if (myPatchType == null) { 1753 throw new InvalidRequestException(Msg.code(1383) + "No patch type supplied, cannot invoke server"); 1754 } 1755 if (myPatchBody == null) { 1756 throw new InvalidRequestException(Msg.code(1384) + "No patch body supplied, cannot invoke server"); 1757 } 1758 1759 BaseHttpClientInvocation invocation; 1760 if (isNotBlank(mySearchUrl)) { 1761 invocation = MethodUtil.createPatchInvocation(myContext, mySearchUrl, myPatchType, myPatchBody); 1762 } else if (myConditional) { 1763 invocation = MethodUtil.createPatchInvocation( 1764 myContext, myPatchType, myPatchBody, myResourceType, getParamMap()); 1765 } else { 1766 if (myId == null || myId.hasIdPart() == false) { 1767 throw new InvalidRequestException( 1768 Msg.code(1385) + "No ID supplied for resource to patch, can not invoke server"); 1769 } 1770 invocation = MethodUtil.createPatchInvocation(myContext, myId, myPatchType, myPatchBody); 1771 } 1772 1773 addPreferHeader(myPrefer, invocation); 1774 1775 OutcomeResponseHandler binding = new OutcomeResponseHandler(myPrefer); 1776 1777 Map<String, List<String>> params = new HashMap<>(); 1778 return invoke(params, binding, invocation); 1779 } 1780 1781 @Override 1782 public IPatchExecutable prefer(PreferReturnEnum theReturn) { 1783 myPrefer = theReturn; 1784 return this; 1785 } 1786 1787 @Override 1788 public IPatchWithBody withBody(String thePatchBody) { 1789 Validate.notBlank(thePatchBody, "thePatchBody must not be blank"); 1790 1791 myPatchBody = thePatchBody; 1792 1793 EncodingEnum encoding = EncodingEnum.detectEncodingNoDefault(thePatchBody); 1794 if (encoding == EncodingEnum.XML) { 1795 myPatchType = PatchTypeEnum.XML_PATCH; 1796 } else if (encoding == EncodingEnum.JSON) { 1797 myPatchType = PatchTypeEnum.JSON_PATCH; 1798 } else { 1799 throw new IllegalArgumentException(Msg.code(1386) + "Unable to determine encoding of patch"); 1800 } 1801 1802 return this; 1803 } 1804 1805 @Override 1806 public IPatchWithBody withFhirPatch(IBaseParameters thePatchBody) { 1807 Validate.notNull(thePatchBody, "thePatchBody must not be null"); 1808 1809 myPatchType = PatchTypeEnum.FHIR_PATCH_JSON; 1810 myPatchBody = myContext.newJsonParser().encodeResourceToString(thePatchBody); 1811 1812 return this; 1813 } 1814 1815 @Override 1816 public IPatchExecutable withId(IIdType theId) { 1817 if (theId == null) { 1818 throw new NullPointerException(Msg.code(1387) + "theId can not be null"); 1819 } 1820 Validate.notBlank( 1821 theId.getIdPart(), 1822 "theId must not be blank and must contain a resource type and ID (e.g. \"Patient/123\"), found: %s", 1823 UrlUtil.sanitizeUrlPart(theId.getValue())); 1824 Validate.notBlank( 1825 theId.getResourceType(), 1826 "theId must not be blank and must contain a resource type and ID (e.g. \"Patient/123\"), found: %s", 1827 UrlUtil.sanitizeUrlPart(theId.getValue())); 1828 myId = theId; 1829 return this; 1830 } 1831 1832 @Override 1833 public IPatchExecutable withId(String theId) { 1834 if (theId == null) { 1835 throw new NullPointerException(Msg.code(1388) + "theId can not be null"); 1836 } 1837 return withId(new IdDt(theId)); 1838 } 1839 } 1840 1841 @SuppressWarnings({"rawtypes", "unchecked"}) 1842 private class ReadInternal extends BaseClientExecutable implements IRead, IReadTyped, IReadExecutable { 1843 private IIdType myId; 1844 private String myIfVersionMatches; 1845 private ICallable myNotModifiedHandler; 1846 private RuntimeResourceDefinition myType; 1847 1848 @Override 1849 public Object execute() { // AAA 1850 if (myId.hasVersionIdPart()) { 1851 return doReadOrVRead( 1852 myType.getImplementingClass(), 1853 myId, 1854 true, 1855 myNotModifiedHandler, 1856 myIfVersionMatches, 1857 myPrettyPrint, 1858 mySummaryMode, 1859 myParamEncoding, 1860 getSubsetElements(), 1861 getCustomAcceptHeaderValue(), 1862 myCustomHeaderValues); 1863 } 1864 return doReadOrVRead( 1865 myType.getImplementingClass(), 1866 myId, 1867 false, 1868 myNotModifiedHandler, 1869 myIfVersionMatches, 1870 myPrettyPrint, 1871 mySummaryMode, 1872 myParamEncoding, 1873 getSubsetElements(), 1874 getCustomAcceptHeaderValue(), 1875 myCustomHeaderValues); 1876 } 1877 1878 @Override 1879 public IReadIfNoneMatch ifVersionMatches(String theVersion) { 1880 myIfVersionMatches = theVersion; 1881 return new IReadIfNoneMatch() { 1882 1883 @Override 1884 public IReadExecutable returnNull() { 1885 myNotModifiedHandler = new ICallable() { 1886 @Override 1887 public Object call() { 1888 return null; 1889 } 1890 }; 1891 return ReadInternal.this; 1892 } 1893 1894 @Override 1895 public IReadExecutable returnResource(final IBaseResource theInstance) { 1896 myNotModifiedHandler = new ICallable() { 1897 @Override 1898 public Object call() { 1899 return theInstance; 1900 } 1901 }; 1902 return ReadInternal.this; 1903 } 1904 1905 @Override 1906 public IReadExecutable throwNotModifiedException() { 1907 myNotModifiedHandler = null; 1908 return ReadInternal.this; 1909 } 1910 }; 1911 } 1912 1913 private void processUrl() { 1914 String resourceType = myId.getResourceType(); 1915 if (isBlank(resourceType)) { 1916 throw new IllegalArgumentException( 1917 Msg.code(1389) + myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, myId)); 1918 } 1919 myType = myContext.getResourceDefinition(resourceType); 1920 if (myType == null) { 1921 throw new IllegalArgumentException( 1922 Msg.code(1390) + myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, myId)); 1923 } 1924 } 1925 1926 @Override 1927 public <T extends IBaseResource> IReadTyped<T> resource(Class<T> theResourceType) { 1928 Validate.notNull(theResourceType, "theResourceType must not be null"); 1929 myType = myContext.getResourceDefinition(theResourceType); 1930 if (myType == null) { 1931 throw new IllegalArgumentException(Msg.code(1391) 1932 + myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); 1933 } 1934 return this; 1935 } 1936 1937 @Override 1938 public IReadTyped<IBaseResource> resource(String theResourceAsText) { 1939 Validate.notBlank(theResourceAsText, "You must supply a value for theResourceAsText"); 1940 myType = myContext.getResourceDefinition(theResourceAsText); 1941 if (myType == null) { 1942 throw new IllegalArgumentException(Msg.code(1392) 1943 + myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceAsText)); 1944 } 1945 return this; 1946 } 1947 1948 @Override 1949 public IReadExecutable withId(IIdType theId) { 1950 Validate.notNull(theId, "The ID can not be null"); 1951 Validate.notBlank(theId.getIdPart(), "The ID can not be blank"); 1952 myId = theId.toUnqualified(); 1953 return this; 1954 } 1955 1956 @Override 1957 public IReadExecutable withId(Long theId) { 1958 Validate.notNull(theId, "The ID can not be null"); 1959 myId = new IdDt(myType.getName(), theId); 1960 return this; 1961 } 1962 1963 @Override 1964 public IReadExecutable withId(String theId) { 1965 Validate.notBlank(theId, "The ID can not be blank"); 1966 if (theId.indexOf('/') == -1) { 1967 myId = new IdDt(myType.getName(), theId); 1968 } else { 1969 myId = new IdDt(theId); 1970 } 1971 return this; 1972 } 1973 1974 @Override 1975 public IReadExecutable withIdAndVersion(String theId, String theVersion) { 1976 Validate.notBlank(theId, "The ID can not be blank"); 1977 myId = new IdDt(myType.getName(), theId, theVersion); 1978 return this; 1979 } 1980 1981 @Override 1982 public IReadExecutable withUrl(IIdType theUrl) { 1983 Validate.notNull(theUrl, "theUrl can not be null"); 1984 myId = theUrl; 1985 processUrl(); 1986 return this; 1987 } 1988 1989 @Override 1990 public IReadExecutable withUrl(String theUrl) { 1991 myId = new IdDt(theUrl); 1992 processUrl(); 1993 return this; 1994 } 1995 } 1996 1997 private final class ResourceListResponseHandler implements IClientResponseHandler<List<IBaseResource>> { 1998 1999 @SuppressWarnings("unchecked") 2000 @Override 2001 public List<IBaseResource> invokeClient( 2002 String theResponseMimeType, 2003 InputStream theResponseInputStream, 2004 int theResponseStatusCode, 2005 Map<String, List<String>> theHeaders) 2006 throws BaseServerResponseException { 2007 Class<? extends IBaseResource> bundleType = 2008 myContext.getResourceDefinition("Bundle").getImplementingClass(); 2009 ResourceResponseHandler<IBaseResource> handler = 2010 new ResourceResponseHandler<>((Class<IBaseResource>) bundleType); 2011 IBaseResource response = handler.invokeClient( 2012 theResponseMimeType, theResponseInputStream, theResponseStatusCode, theHeaders); 2013 IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory(); 2014 bundleFactory.initializeWithBundleResource(response); 2015 return bundleFactory.toListOfResources(); 2016 } 2017 } 2018 2019 @SuppressWarnings({"rawtypes", "unchecked"}) 2020 private class SearchInternal<OUTPUT> extends BaseSearch<IQuery<OUTPUT>, IQuery<OUTPUT>, OUTPUT> 2021 implements IQuery<OUTPUT>, IUntypedQuery<IQuery<OUTPUT>> { 2022 2023 private String myCompartmentName; 2024 private List<Include> myInclude = new ArrayList<>(); 2025 private DateRangeParam myLastUpdated; 2026 private Integer myParamLimit; 2027 private Integer myParamOffset; 2028 private List<Collection<String>> myProfiles = new ArrayList<>(); 2029 private String myResourceId; 2030 private String myResourceName; 2031 private Class<? extends IBaseResource> myResourceType; 2032 private Class<? extends IBaseBundle> myReturnBundleType; 2033 private List<Include> myRevInclude = new ArrayList<>(); 2034 private SearchStyleEnum mySearchStyle; 2035 private String mySearchUrl; 2036 private List<TokenParam> mySecurity = new ArrayList<>(); 2037 private List<SortInternal> mySort = new ArrayList<>(); 2038 private List<TokenParam> myTags = new ArrayList<>(); 2039 private SearchTotalModeEnum myTotalMode; 2040 2041 public SearchInternal() { 2042 myResourceType = null; 2043 myResourceName = null; 2044 mySearchUrl = null; 2045 } 2046 2047 @Override 2048 public IQuery byUrl(String theSearchUrl) { 2049 Validate.notBlank(theSearchUrl, "theSearchUrl must not be blank/null"); 2050 2051 if (theSearchUrl.startsWith("http://") || theSearchUrl.startsWith("https://")) { 2052 mySearchUrl = theSearchUrl; 2053 int qIndex = mySearchUrl.indexOf('?'); 2054 if (qIndex != -1) { 2055 mySearchUrl = mySearchUrl.substring(0, qIndex) 2056 + validateAndEscapeConditionalUrl(mySearchUrl.substring(qIndex)); 2057 } 2058 } else { 2059 String searchUrl = theSearchUrl; 2060 if (searchUrl.startsWith("/")) { 2061 searchUrl = searchUrl.substring(1); 2062 } 2063 if (!searchUrl.matches("[a-zA-Z]+($|\\?.*)")) { 2064 throw new IllegalArgumentException( 2065 Msg.code(1393) 2066 + "Search URL must be either a complete URL starting with http: or https:, or a relative FHIR URL in the form [ResourceType]?[Params]"); 2067 } 2068 int qIndex = searchUrl.indexOf('?'); 2069 if (qIndex == -1) { 2070 mySearchUrl = getUrlBase() + '/' + searchUrl; 2071 } else { 2072 mySearchUrl = getUrlBase() + '/' + validateAndEscapeConditionalUrl(searchUrl); 2073 } 2074 } 2075 return this; 2076 } 2077 2078 @Override 2079 public IQuery count(int theLimitTo) { 2080 if (theLimitTo > 0) { 2081 myParamLimit = theLimitTo; 2082 } else { 2083 myParamLimit = null; 2084 } 2085 return this; 2086 } 2087 2088 @Override 2089 public IQuery offset(int theOffset) { 2090 if (theOffset >= 0) { 2091 myParamOffset = theOffset; 2092 } else { 2093 myParamOffset = null; 2094 } 2095 return this; 2096 } 2097 2098 @Override 2099 public OUTPUT execute() { 2100 2101 Map<String, List<String>> params = getParamMap(); 2102 2103 for (TokenParam next : myTags) { 2104 addParam(params, Constants.PARAM_TAG, next.getValueAsQueryToken(myContext)); 2105 } 2106 2107 for (TokenParam next : mySecurity) { 2108 addParam(params, Constants.PARAM_SECURITY, next.getValueAsQueryToken(myContext)); 2109 } 2110 2111 for (Collection<String> profileUris : myProfiles) { 2112 StringBuilder builder = new StringBuilder(); 2113 for (Iterator<String> profileItr = profileUris.iterator(); profileItr.hasNext(); ) { 2114 builder.append(profileItr.next()); 2115 if (profileItr.hasNext()) { 2116 builder.append(','); 2117 } 2118 } 2119 addParam(params, Constants.PARAM_PROFILE, builder.toString()); 2120 } 2121 2122 for (Include next : myInclude) { 2123 if (next.isRecurse()) { 2124 if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) { 2125 addParam(params, Constants.PARAM_INCLUDE_ITERATE, next.getValue()); 2126 } else { 2127 addParam(params, Constants.PARAM_INCLUDE_RECURSE, next.getValue()); 2128 } 2129 } else { 2130 addParam(params, Constants.PARAM_INCLUDE, next.getValue()); 2131 } 2132 } 2133 2134 for (Include next : myRevInclude) { 2135 if (next.isRecurse()) { 2136 if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) { 2137 addParam(params, Constants.PARAM_REVINCLUDE_ITERATE, next.getValue()); 2138 } else { 2139 addParam(params, Constants.PARAM_REVINCLUDE_RECURSE, next.getValue()); 2140 } 2141 } else { 2142 addParam(params, Constants.PARAM_REVINCLUDE, next.getValue()); 2143 } 2144 } 2145 2146 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) { 2147 SortSpec rootSs = null; 2148 SortSpec lastSs = null; 2149 for (SortInternal next : mySort) { 2150 SortSpec nextSortSpec = new SortSpec(); 2151 nextSortSpec.setParamName(next.getParamValue()); 2152 nextSortSpec.setOrder(next.getDirection()); 2153 if (rootSs == null) { 2154 rootSs = nextSortSpec; 2155 } else { 2156 lastSs.setChain(nextSortSpec); 2157 } 2158 lastSs = nextSortSpec; 2159 } 2160 if (rootSs != null) { 2161 addParam(params, Constants.PARAM_SORT, SortParameter.createSortStringDstu3(rootSs)); 2162 } 2163 } else { 2164 for (SortInternal next : mySort) { 2165 addParam(params, next.getParamName(), next.getParamValue()); 2166 } 2167 } 2168 2169 if (myParamLimit != null) { 2170 addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit)); 2171 } 2172 2173 if (myParamOffset != null) { 2174 addParam(params, Constants.PARAM_OFFSET, Integer.toString(myParamOffset)); 2175 } 2176 2177 if (myLastUpdated != null) { 2178 for (DateParam next : myLastUpdated.getValuesAsQueryTokens()) { 2179 addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken(myContext)); 2180 } 2181 } 2182 2183 if (myTotalMode != null) { 2184 addParam(params, Constants.PARAM_SEARCH_TOTAL_MODE, myTotalMode.getCode()); 2185 } 2186 2187 IClientResponseHandler<? extends IBase> binding; 2188 binding = new ResourceResponseHandler(myReturnBundleType, getPreferResponseTypes(myResourceType)); 2189 2190 IdDt resourceId = myResourceId != null ? new IdDt(myResourceId) : null; 2191 2192 BaseHttpClientInvocation invocation; 2193 if (mySearchUrl != null) { 2194 invocation = SearchMethodBinding.createSearchInvocation( 2195 myContext, mySearchUrl, UrlSourceEnum.EXPLICIT, params); 2196 } else { 2197 invocation = SearchMethodBinding.createSearchInvocation( 2198 myContext, myResourceName, params, resourceId, myCompartmentName, mySearchStyle); 2199 } 2200 2201 return (OUTPUT) invoke(params, binding, invocation); 2202 } 2203 2204 @Override 2205 public IQuery forAllResources() { 2206 return this; 2207 } 2208 2209 @Override 2210 public IQuery forResource(Class theResourceType) { 2211 setType(theResourceType); 2212 return this; 2213 } 2214 2215 @Override 2216 public IQuery forResource(String theResourceName) { 2217 setType(theResourceName); 2218 return this; 2219 } 2220 2221 @Override 2222 public IQuery include(Include theInclude) { 2223 myInclude.add(theInclude); 2224 return this; 2225 } 2226 2227 @Override 2228 public IQuery lastUpdated(DateRangeParam theLastUpdated) { 2229 myLastUpdated = theLastUpdated; 2230 return this; 2231 } 2232 2233 @Deprecated // override deprecated method 2234 @Override 2235 public IQuery limitTo(int theLimitTo) { 2236 return count(theLimitTo); 2237 } 2238 2239 @Override 2240 public IQuery<OUTPUT> totalMode(SearchTotalModeEnum theSearchTotalModeEnum) { 2241 myTotalMode = theSearchTotalModeEnum; 2242 return this; 2243 } 2244 2245 @Override 2246 public IQuery returnBundle(Class theClass) { 2247 if (theClass == null) { 2248 throw new NullPointerException(Msg.code(1394) + "theClass must not be null"); 2249 } 2250 myReturnBundleType = theClass; 2251 return this; 2252 } 2253 2254 @Override 2255 public IQuery revInclude(Include theInclude) { 2256 myRevInclude.add(theInclude); 2257 return this; 2258 } 2259 2260 private void setType(Class<? extends IBaseResource> theResourceType) { 2261 myResourceType = theResourceType; 2262 RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType); 2263 myResourceName = definition.getName(); 2264 } 2265 2266 private void setType(String theResourceName) { 2267 myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass(); 2268 myResourceName = theResourceName; 2269 } 2270 2271 @Override 2272 public ISort sort() { 2273 SortInternal retVal = new SortInternal(this); 2274 mySort.add(retVal); 2275 return retVal; 2276 } 2277 2278 @Override 2279 public IQuery sort(SortSpec theSortSpec) { 2280 SortSpec sortSpec = theSortSpec; 2281 while (sortSpec != null) { 2282 mySort.add(new SortInternal(sortSpec)); 2283 sortSpec = sortSpec.getChain(); 2284 } 2285 return this; 2286 } 2287 2288 @Override 2289 public IQuery usingStyle(SearchStyleEnum theStyle) { 2290 mySearchStyle = theStyle; 2291 return this; 2292 } 2293 2294 @Override 2295 public IQuery withAnyProfile(Collection theProfileUris) { 2296 Validate.notEmpty(theProfileUris, "theProfileUris must not be null or empty"); 2297 myProfiles.add(theProfileUris); 2298 return this; 2299 } 2300 2301 @Override 2302 public IQuery withIdAndCompartment(String theResourceId, String theCompartmentName) { 2303 myResourceId = theResourceId; 2304 myCompartmentName = theCompartmentName; 2305 return this; 2306 } 2307 2308 @Override 2309 public IQuery withProfile(String theProfileUri) { 2310 Validate.notBlank(theProfileUri, "theProfileUri must not be null or empty"); 2311 myProfiles.add(Collections.singletonList(theProfileUri)); 2312 return this; 2313 } 2314 2315 @Override 2316 public IQuery withSecurity(String theSystem, String theCode) { 2317 Validate.notBlank(theCode, "theCode must not be null or empty"); 2318 mySecurity.add(new TokenParam(theSystem, theCode)); 2319 return this; 2320 } 2321 2322 @Override 2323 public IQuery withTag(String theSystem, String theCode) { 2324 Validate.notBlank(theCode, "theCode must not be null or empty"); 2325 myTags.add(new TokenParam(theSystem, theCode)); 2326 return this; 2327 } 2328 } 2329 2330 private final class StringResponseHandler implements IClientResponseHandler<String> { 2331 2332 @Override 2333 public String invokeClient( 2334 String theResponseMimeType, 2335 InputStream theResponseInputStream, 2336 int theResponseStatusCode, 2337 Map<String, List<String>> theHeaders) 2338 throws IOException, BaseServerResponseException { 2339 return IOUtils.toString(theResponseInputStream, Charsets.UTF_8); 2340 } 2341 } 2342 2343 private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T> 2344 implements ITransactionTyped<T> { 2345 2346 private IBaseBundle myBaseBundle; 2347 private String myRawBundle; 2348 private EncodingEnum myRawBundleEncoding; 2349 private List<? extends IBaseResource> myResources; 2350 2351 public TransactionExecutable(IBaseBundle theBundle) { 2352 myBaseBundle = theBundle; 2353 } 2354 2355 public TransactionExecutable(List<? extends IBaseResource> theResources) { 2356 myResources = theResources; 2357 } 2358 2359 public TransactionExecutable(String theBundle) { 2360 myRawBundle = theBundle; 2361 myRawBundleEncoding = EncodingEnum.detectEncodingNoDefault(myRawBundle); 2362 if (myRawBundleEncoding == null) { 2363 throw new IllegalArgumentException(Msg.code(1395) 2364 + myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); 2365 } 2366 } 2367 2368 @SuppressWarnings({"unchecked", "rawtypes"}) 2369 @Override 2370 public T execute() { 2371 Map<String, List<String>> params = new HashMap<String, List<String>>(); 2372 if (myResources != null) { 2373 ResourceListResponseHandler binding = new ResourceListResponseHandler(); 2374 BaseHttpClientInvocation invocation = 2375 TransactionMethodBinding.createTransactionInvocation(myResources, myContext); 2376 return (T) invoke(params, binding, invocation); 2377 } else if (myBaseBundle != null) { 2378 ResourceResponseHandler binding = 2379 new ResourceResponseHandler(myBaseBundle.getClass(), getPreferResponseTypes()); 2380 BaseHttpClientInvocation invocation = 2381 TransactionMethodBinding.createTransactionInvocation(myBaseBundle, myContext); 2382 return (T) invoke(params, binding, invocation); 2383 // } else if (myRawBundle != null) { 2384 } else { 2385 StringResponseHandler binding = new StringResponseHandler(); 2386 /* 2387 * If the user has explicitly requested a given encoding, we may need to re-encode the raw string 2388 */ 2389 if (getParamEncoding() != null) { 2390 if (EncodingEnum.detectEncodingNoDefault(myRawBundle) != getParamEncoding()) { 2391 IBaseResource parsed = parseResourceBody(myRawBundle); 2392 myRawBundle = 2393 getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed); 2394 } 2395 } 2396 BaseHttpClientInvocation invocation = 2397 TransactionMethodBinding.createTransactionInvocation(myRawBundle, myContext); 2398 return (T) invoke(params, binding, invocation); 2399 } 2400 } 2401 } 2402 2403 private final class TransactionInternal implements ITransaction { 2404 2405 @Override 2406 public ITransactionTyped<String> withBundle(String theBundle) { 2407 Validate.notBlank(theBundle, "theBundle must not be null"); 2408 return new TransactionExecutable<String>(theBundle); 2409 } 2410 2411 @Override 2412 public <T extends IBaseBundle> ITransactionTyped<T> withBundle(T theBundle) { 2413 Validate.notNull(theBundle, "theBundle must not be null"); 2414 return new TransactionExecutable<T>(theBundle); 2415 } 2416 2417 @Override 2418 public ITransactionTyped<List<IBaseResource>> withResources(List<? extends IBaseResource> theResources) { 2419 Validate.notNull(theResources, "theResources must not be null"); 2420 2421 for (IBaseResource next : theResources) { 2422 BundleEntryTransactionMethodEnum entryMethod = 2423 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); 2424 2425 if (entryMethod == null) { 2426 if (isBlank(next.getIdElement().getValue())) { 2427 entryMethod = BundleEntryTransactionMethodEnum.POST; 2428 } else { 2429 entryMethod = BundleEntryTransactionMethodEnum.PUT; 2430 } 2431 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(next, entryMethod); 2432 } 2433 } 2434 2435 return new TransactionExecutable<>(theResources); 2436 } 2437 } 2438 2439 private class UpdateInternal extends BaseSearch<IUpdateExecutable, IUpdateWithQueryTyped, MethodOutcome> 2440 implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped { 2441 2442 private boolean myConditional; 2443 private IIdType myId; 2444 private PreferReturnEnum myPrefer; 2445 private IBaseResource myResource; 2446 private String myResourceBody; 2447 private String mySearchUrl; 2448 private boolean myIsHistoryRewrite; 2449 2450 @Override 2451 public IUpdateTyped historyRewrite() { 2452 myIsHistoryRewrite = true; 2453 return this; 2454 } 2455 2456 @Override 2457 public IUpdateWithQuery conditional() { 2458 myConditional = true; 2459 return this; 2460 } 2461 2462 @Override 2463 public IUpdateTyped conditionalByUrl(String theSearchUrl) { 2464 mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); 2465 return this; 2466 } 2467 2468 @Override 2469 public MethodOutcome execute() { 2470 if (myResource == null) { 2471 myResource = parseResourceBody(myResourceBody); 2472 } 2473 2474 // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding 2475 if (getParamEncoding() != null) { 2476 myResourceBody = null; 2477 } 2478 2479 BaseHttpClientInvocation invocation; 2480 if (mySearchUrl != null) { 2481 invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, mySearchUrl); 2482 } else if (myConditional) { 2483 invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, getParamMap()); 2484 } else { 2485 if (myId == null) { 2486 myId = myResource.getIdElement(); 2487 } 2488 2489 if (myId == null || myId.hasIdPart() == false) { 2490 throw new InvalidRequestException( 2491 Msg.code(1396) + "No ID supplied for resource to update, can not invoke server"); 2492 } 2493 2494 if (myIsHistoryRewrite) { 2495 if (!myId.hasVersionIdPart()) { 2496 throw new InvalidRequestException(Msg.code(2090) + "ID must contain a history version, found: " 2497 + myId.getVersionIdPart()); 2498 } 2499 invocation = MethodUtil.createUpdateHistoryRewriteInvocation( 2500 myResource, myResourceBody, myId, myContext); 2501 invocation.addHeader(Constants.HEADER_REWRITE_HISTORY, "true"); 2502 } else { 2503 invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext); 2504 } 2505 } 2506 2507 addPreferHeader(myPrefer, invocation); 2508 2509 OutcomeResponseHandler binding = new OutcomeResponseHandler(myPrefer); 2510 2511 Map<String, List<String>> params = new HashMap<>(); 2512 return invoke(params, binding, invocation); 2513 } 2514 2515 @Override 2516 public IUpdateExecutable prefer(PreferReturnEnum theReturn) { 2517 myPrefer = theReturn; 2518 return this; 2519 } 2520 2521 @Override 2522 public IUpdateTyped resource(IBaseResource theResource) { 2523 Validate.notNull(theResource, "Resource can not be null"); 2524 myResource = theResource; 2525 return this; 2526 } 2527 2528 @Override 2529 public IUpdateTyped resource(String theResourceBody) { 2530 Validate.notBlank(theResourceBody, "Body can not be null or blank"); 2531 myResourceBody = theResourceBody; 2532 return this; 2533 } 2534 2535 @Override 2536 public IUpdateExecutable withId(IIdType theId) { 2537 if (theId == null) { 2538 throw new NullPointerException(Msg.code(1397) + "theId can not be null"); 2539 } 2540 if (theId.hasIdPart() == false) { 2541 throw new NullPointerException( 2542 Msg.code(1398) + "theId must not be blank and must contain an ID, found: " + theId.getValue()); 2543 } 2544 myId = theId; 2545 return this; 2546 } 2547 2548 @Override 2549 public IUpdateExecutable withId(String theId) { 2550 if (theId == null) { 2551 throw new NullPointerException(Msg.code(1399) + "theId can not be null"); 2552 } 2553 if (isBlank(theId)) { 2554 throw new NullPointerException( 2555 Msg.code(1400) + "theId must not be blank and must contain an ID, found: " + theId); 2556 } 2557 myId = new IdDt(theId); 2558 return this; 2559 } 2560 } 2561 2562 private class ValidateInternal extends BaseClientExecutable<IValidateUntyped, MethodOutcome> 2563 implements IValidate, IValidateUntyped { 2564 private IBaseResource myResource; 2565 2566 @Override 2567 public MethodOutcome execute() { 2568 BaseHttpClientInvocation invocation = 2569 ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, myResource); 2570 ResourceResponseHandler<BaseOperationOutcome> handler = new ResourceResponseHandler<>(null, null); 2571 MethodOutcomeResponseHandler methodHandler = new MethodOutcomeResponseHandler(handler); 2572 return invoke(null, methodHandler, invocation); 2573 } 2574 2575 @Override 2576 public IValidateUntyped resource(IBaseResource theResource) { 2577 Validate.notNull(theResource, "theResource must not be null"); 2578 myResource = theResource; 2579 return this; 2580 } 2581 2582 @Override 2583 public IValidateUntyped resource(String theResourceRaw) { 2584 Validate.notBlank(theResourceRaw, "theResourceRaw must not be null or blank"); 2585 myResource = parseResourceBody(theResourceRaw); 2586 2587 EncodingEnum enc = EncodingEnum.detectEncodingNoDefault(theResourceRaw); 2588 if (enc == null) { 2589 throw new IllegalArgumentException(Msg.code(1401) 2590 + myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); 2591 } 2592 switch (enc) { 2593 case XML: 2594 encodedXml(); 2595 break; 2596 case JSON: 2597 encodedJson(); 2598 break; 2599 } 2600 return this; 2601 } 2602 } 2603 2604 @SuppressWarnings("rawtypes") 2605 private static class SortInternal implements ISort { 2606 2607 private SortOrderEnum myDirection; 2608 private SearchInternal myFor; 2609 private String myParamName; 2610 private String myParamValue; 2611 2612 public SortInternal(SearchInternal theFor) { 2613 myFor = theFor; 2614 } 2615 2616 public SortInternal(SortSpec theSortSpec) { 2617 if (theSortSpec.getOrder() == null) { 2618 myParamName = Constants.PARAM_SORT; 2619 } else if (theSortSpec.getOrder() == SortOrderEnum.ASC) { 2620 myParamName = Constants.PARAM_SORT_ASC; 2621 } else if (theSortSpec.getOrder() == SortOrderEnum.DESC) { 2622 myParamName = Constants.PARAM_SORT_DESC; 2623 } 2624 myDirection = theSortSpec.getOrder(); 2625 myParamValue = theSortSpec.getParamName(); 2626 } 2627 2628 @Override 2629 public IQuery ascending(IParam theParam) { 2630 myParamName = Constants.PARAM_SORT_ASC; 2631 myDirection = SortOrderEnum.ASC; 2632 myParamValue = theParam.getParamName(); 2633 return myFor; 2634 } 2635 2636 @Override 2637 public IQuery ascending(String theParam) { 2638 myParamName = Constants.PARAM_SORT_ASC; 2639 myDirection = SortOrderEnum.ASC; 2640 myParamValue = theParam; 2641 return myFor; 2642 } 2643 2644 @Override 2645 public IQuery defaultOrder(IParam theParam) { 2646 myParamName = Constants.PARAM_SORT; 2647 myDirection = null; 2648 myParamValue = theParam.getParamName(); 2649 return myFor; 2650 } 2651 2652 @Override 2653 public IQuery defaultOrder(String theParam) { 2654 myParamName = Constants.PARAM_SORT; 2655 myDirection = null; 2656 myParamValue = theParam; 2657 return myFor; 2658 } 2659 2660 @Override 2661 public IQuery descending(IParam theParam) { 2662 myParamName = Constants.PARAM_SORT_DESC; 2663 myDirection = SortOrderEnum.DESC; 2664 myParamValue = theParam.getParamName(); 2665 return myFor; 2666 } 2667 2668 @Override 2669 public IQuery descending(String theParam) { 2670 myParamName = Constants.PARAM_SORT_DESC; 2671 myDirection = SortOrderEnum.DESC; 2672 myParamValue = theParam; 2673 return myFor; 2674 } 2675 2676 public SortOrderEnum getDirection() { 2677 return myDirection; 2678 } 2679 2680 public String getParamName() { 2681 return myParamName; 2682 } 2683 2684 public String getParamValue() { 2685 return myParamValue; 2686 } 2687 } 2688 2689 private static void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) { 2690 if (!params.containsKey(parameterName)) { 2691 params.put(parameterName, new ArrayList<>()); 2692 } 2693 params.get(parameterName).add(parameterValue); 2694 } 2695 2696 private static void addPreferHeader(PreferReturnEnum thePrefer, BaseHttpClientInvocation theInvocation) { 2697 if (thePrefer != null) { 2698 theInvocation.addHeader( 2699 Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + thePrefer.getHeaderValue()); 2700 } 2701 } 2702 2703 private static String validateAndEscapeConditionalUrl(String theSearchUrl) { 2704 Validate.notBlank(theSearchUrl, "Conditional URL can not be blank/null"); 2705 StringBuilder b = new StringBuilder(); 2706 boolean haveHadQuestionMark = false; 2707 for (int i = 0; i < theSearchUrl.length(); i++) { 2708 char nextChar = theSearchUrl.charAt(i); 2709 if (!haveHadQuestionMark) { 2710 if (nextChar == '?') { 2711 haveHadQuestionMark = true; 2712 } else if (!Character.isLetter(nextChar)) { 2713 throw new IllegalArgumentException(Msg.code(1402) 2714 + "Conditional URL must be in the format \"[ResourceType]?[Params]\" and must not have a base URL - Found: " 2715 + theSearchUrl); 2716 } 2717 b.append(nextChar); 2718 } else { 2719 switch (nextChar) { 2720 case '|': 2721 case '?': 2722 case '$': 2723 case ':': 2724 b.append(UrlUtil.escapeUrlParam(Character.toString(nextChar))); 2725 break; 2726 default: 2727 b.append(nextChar); 2728 break; 2729 } 2730 } 2731 } 2732 return b.toString(); 2733 } 2734}