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