From 59d29c45b3d9dc0c31a99538a7080ef4d91e50d3 Mon Sep 17 00:00:00 2001 From: Hugo Soares Date: Wed, 18 Jan 2017 19:33:56 +0000 Subject: [PATCH 1/3] Feature Request: Add client support for $process-message special operation #407 Changed client to send messages to a processing server: Object response = client .operation() .onServer() .processMessage() .setResponseUrlParam("http://myserver/fhir") .setMessageBundle(msgBundle) .execute(); --- .../ca/uhn/fhir/rest/client/BaseClient.java | 24 ++ .../uhn/fhir/rest/client/GenericClient.java | 118 +++++++--- .../fhir/rest/client/api/IRestfulClient.java | 16 ++ .../rest/gclient/IOperationProcessMsg.java | 24 ++ .../fhir/rest/gclient/IOperationUnnamed.java | 1 + .../rest/method/OperationMethodBinding.java | 16 ++ .../ca/uhn/fhir/rest/server/Constants.java | 3 + .../rest/client/GenericClientDstu2Test.java | 10 + .../rest/client/MessageClientDstu2Test.java | 213 ++++++++++++++++++ 9 files changed, 395 insertions(+), 30 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java create mode 100644 hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java index 7af96417244..4649ce4d674 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java @@ -90,6 +90,8 @@ public abstract class BaseClient implements IRestfulClient { private Boolean myPrettyPrint = false; private SummaryEnum mySummary; private final String myUrlBase; + private Boolean myAsync = null; + private String myResponseUrl = null; BaseClient(IHttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) { super(); @@ -235,6 +237,17 @@ public abstract class BaseClient implements IRestfulClient { if (theSubsetElements != null && theSubsetElements.isEmpty() == false) { params.put(Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ','))); } + + /* + Message Client Params + */ + if(myAsync != null && myAsync == true ) { + params.put(Constants.PARAM_ASYNC, Collections.singletonList(myAsync.toString())); + } + + if(myResponseUrl != null && !myResponseUrl.isEmpty() ) { + params.put(Constants.PARAM_RESPONSE_URL, Collections.singletonList(myResponseUrl)); + } EncodingEnum encoding = getEncoding(); if (theEncoding != null) { @@ -470,6 +483,7 @@ public abstract class BaseClient implements IRestfulClient { public void setSummary(SummaryEnum theSummary) { mySummary = theSummary; } + @Override public void unregisterInterceptor(IClientInterceptor theInterceptor) { @@ -485,6 +499,16 @@ public abstract class BaseClient implements IRestfulClient { return preferResponseTypes; } + @Override + public void setMessageAsync(Boolean isAsync) { + myAsync = isAsync; + } + + @Override + public void setMessageResponseUrl(String responseUrl) { + myResponseUrl = responseUrl; + } + protected final class ResourceResponseHandler implements IClientResponseHandler { private boolean myAllowHtmlResponse; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index 42c2a03ccb9..3df4ce8513a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -66,6 +66,7 @@ import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; +import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.MethodOutcome; @@ -102,6 +103,7 @@ import ca.uhn.fhir.rest.gclient.IMetaAddOrDeleteSourced; import ca.uhn.fhir.rest.gclient.IMetaAddOrDeleteUnsourced; import ca.uhn.fhir.rest.gclient.IMetaGetUnsourced; import ca.uhn.fhir.rest.gclient.IOperation; +import ca.uhn.fhir.rest.gclient.IOperationProcessMsg; import ca.uhn.fhir.rest.gclient.IOperationUnnamed; import ca.uhn.fhir.rest.gclient.IOperationUntyped; import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput; @@ -1443,7 +1445,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } @SuppressWarnings("rawtypes") - private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput, IOperationUntypedWithInputAndPartialOutput { + private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput, IOperationUntypedWithInputAndPartialOutput, IOperationProcessMsg { private IIdType myId; private String myOperationName; @@ -1451,6 +1453,8 @@ public class GenericClient extends BaseClient implements IGenericClient { private RuntimeResourceDefinition myParametersDef; private Class myType; private boolean myUseHttpGet; + private IBaseBundle myMsgBundle; + @SuppressWarnings("unchecked") private void addParam(String theName, IBase theValue) { @@ -1505,41 +1509,58 @@ public class GenericClient extends BaseClient implements IGenericClient { @SuppressWarnings("unchecked") @Override public Object execute() { - String resourceName; - String id; - if (myType != null) { - resourceName = myContext.getResourceDefinition(myType).getName(); - id = null; - } else if (myId != null) { - resourceName = myId.getResourceType(); - id = myId.getIdPart(); - } else { - resourceName = null; - id = null; - } + if (myOperationName != null && myOperationName.equals(Constants.EXTOP_PROCESS_MESSAGE)) { + //If is $process-message operation + BaseHttpClientInvocation invocation = OperationMethodBinding.createProcessMsgInvocation(myContext, myOperationName, myMsgBundle); - BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet); + ResourceResponseHandler handler = new ResourceResponseHandler(); + handler.setPreferResponseTypes(getPreferResponseTypes(myType)); + + /* + Map> urlParams = new LinkedHashMap>(); + GenericClient.this.addParam(urlParams, Constants.PARAM_ASYNC, String.valueOf(myAsync)); + GenericClient.this.addParam(urlParams, Constants.PARAM_RESPONSE_URL, String.valueOf(myRespondToUri)); + */ - ResourceResponseHandler handler = new ResourceResponseHandler(); - handler.setPreferResponseTypes(getPreferResponseTypes(myType)); + Object retVal = invoke(null, handler, invocation); + return retVal; + } else { + String resourceName; + String id; + if (myType != null) { + resourceName = myContext.getResourceDefinition(myType).getName(); + id = null; + } else if (myId != null) { + resourceName = myId.getResourceType(); + id = myId.getIdPart(); + } else { + resourceName = null; + id = null; + } - Object retVal = invoke(null, handler, invocation); - if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { - return retVal; - } else { - RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); - IBaseResource parameters = def.newInstance(); + BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet); - BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); - IBase parameter = paramChildElem.newInstance(); - paramChild.getMutator().addValue(parameters, parameter); + ResourceResponseHandler handler = new ResourceResponseHandler(); + handler.setPreferResponseTypes(getPreferResponseTypes(myType)); - BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); - resourceElem.getMutator().addValue(parameter, (IBase) retVal); + Object retVal = invoke(null, handler, invocation); + if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { + return retVal; + } else { + RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); + IBaseResource parameters = def.newInstance(); - return parameters; - } + BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); + BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); + IBase parameter = paramChildElem.newInstance(); + paramChild.getMutator().addValue(parameters, parameter); + + BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); + resourceElem.getMutator().addValue(parameter, (IBase) retVal); + + return parameters; + } + } } @Override @@ -1548,12 +1569,19 @@ public class GenericClient extends BaseClient implements IGenericClient { myOperationName = theName; return this; } + + @Override + public IOperationProcessMsg processMessage() { + myOperationName = Constants.EXTOP_PROCESS_MESSAGE; + return this; + } @Override public IOperationUnnamed onInstance(IIdType theId) { myId = theId; return this; } + @Override public IOperationUnnamed onServer() { @@ -1625,6 +1653,36 @@ public class GenericClient extends BaseClient implements IGenericClient { return this; } + @Override + public IOperationProcessMsg setMessageBundle(IBaseBundle theMsgBundle) { + + Validate.notNull(theMsgBundle, "theMsgBundle must not be null"); + /* Validate.isTrue(theMsgBundle.getType().getValueAsEnum() == BundleTypeEnum.MESSAGE); + Validate.isTrue(theMsgBundle.getEntries().size() > 0); + Validate.notNull(theMsgBundle.getEntries().get(0).getResource(), "Message Bundle first entry must be a MessageHeader resource"); + Validate.isTrue(theMsgBundle.getEntries().get(0).getResource().getResourceName().equals("MessageHeader"), "Message Bundle first entry must be a MessageHeader resource"); + */ + myMsgBundle = (IBaseBundle) theMsgBundle; + return this; + } + + @Override + public IOperationProcessMsg setAsyncProcessingMode() { + setMessageAsync(true); + return this; + } + + @Override + public IOperationProcessMsg setResponseUrlParam(String responseUrl) { + Validate.notEmpty(responseUrl, "responseUrl must not be null"); + Validate.matchesPattern(responseUrl, "^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "responseUrl must be a valid URL"); + setMessageResponseUrl(responseUrl); + return this; + } + + + + } private final class OperationOutcomeResponseHandler implements IClientResponseHandler { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java index 7b7bc2039ba..09514e76ccd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java @@ -88,6 +88,22 @@ public interface IRestfulClient { * to be applied globally on this client. */ void setSummary(SummaryEnum theSummary); + + /** + * Specifies that the server should process the message Synchronously or Asynchronously. This is only applicable to the + * $process-message server operation + * + * @param isAsync The async print flag to use in the request (default is false) + */ + void setMessageAsync(Boolean isAsync); + + /** + * Specifies that the server should send Asynchronous responses to this url. This is only applicable to the + * $process-message server operation + * + * @param isAsync The async print flag to use in the request (default is false) + */ + void setMessageResponseUrl(String responseUrl); /** * Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java new file mode 100644 index 00000000000..61251b45c98 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package ca.uhn.fhir.rest.gclient; + +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; +import org.hl7.fhir.instance.model.api.IBaseResource; + +/** + * + * @author HGS + */ +public interface IOperationProcessMsg extends IClientExecutable, T> { + + IOperationProcessMsg setMessageBundle(IBaseBundle theMsgBundle); + + IOperationProcessMsg setAsyncProcessingMode(); + + IOperationProcessMsg setResponseUrlParam(String respondToUri); + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java index f746e426ff3..818f39ec9b2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java @@ -24,5 +24,6 @@ package ca.uhn.fhir.rest.gclient; public interface IOperationUnnamed { IOperationUntyped named(String theName); + IOperationProcessMsg processMessage(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java index 534723a01a0..9754962dff4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java @@ -59,6 +59,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.FhirTerser; +import org.hl7.fhir.instance.model.api.IBaseBundle; public class OperationMethodBinding extends BaseResourceReturningMethodBinding { @@ -318,6 +319,21 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { myDescription = theDescription; } + public static BaseHttpClientInvocation createProcessMsgInvocation(FhirContext theContext, String theOperationName, IBaseBundle theInput) { + StringBuilder b = new StringBuilder(); + + if (b.length() > 0) { + b.append('/'); + } + if (!theOperationName.startsWith("$")) { + b.append("$"); + } + b.append(theOperationName); + + + return new HttpPostClientInvocation(theContext, theInput, b.toString()); + + } public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, boolean theUseHttpGet) { StringBuilder b = new StringBuilder(); if (theResourceName != null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 69927cae815..9cb16a0e513 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -139,6 +139,9 @@ public class Constants { public static final String PARAM_TAGS = "_tags"; public static final String PARAM_TEXT = "_text"; public static final String PARAM_VALIDATE = "_validate"; + public static final String PARAM_ASYNC = "async"; //Used in messaging + public static final String PARAM_RESPONSE_URL = "response-url"; //Used in messaging + public static final String EXTOP_PROCESS_MESSAGE = "$process-message"; //Used in messaging public static final String PARAMQUALIFIER_MISSING = ":missing"; public static final String PARAMQUALIFIER_MISSING_FALSE = "false"; public static final String PARAMQUALIFIER_MISSING_TRUE = "true"; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index 9c9c18bd926..e9329c97c96 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -2703,6 +2703,16 @@ public class GenericClientDstu2Test { public void unregisterInterceptor(IClientInterceptor theInterceptor) { //nothing } + + @Override + public void setMessageAsync(Boolean isAsync) { + + } + + @Override + public void setMessageResponseUrl(String responseUrl) { + + } } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java new file mode 100644 index 00000000000..02423967593 --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java @@ -0,0 +1,213 @@ +package ca.uhn.fhir.rest.client; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.ReaderInputStream; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicStatusLine; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.dstu2.resource.Bundle; +import ca.uhn.fhir.model.dstu2.resource.MessageHeader; +import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; +import ca.uhn.fhir.model.dstu2.resource.Parameters; +import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; +import ca.uhn.fhir.model.dstu2.valueset.ResponseTypeEnum; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.model.primitive.StringDt; +import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.EncodingEnum; +import ca.uhn.fhir.util.TestUtil; +import java.util.Date; +import java.util.UUID; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MessageClientDstu2Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MessageClientDstu2Test.class); + private FhirContext ourCtx; + private HttpClient myHttpClient; + + private HttpResponse myHttpResponse; + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @Before + public void before() { + ourCtx = FhirContext.forDstu2(); + + myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs()); + ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx)); + ourCtx.getRestfulClientFactory().setConnectionRequestTimeout(10000); + ourCtx.getRestfulClientFactory().setConnectTimeout(10000); + ourCtx.getRestfulClientFactory().setPoolMaxPerRoute(100); + ourCtx.getRestfulClientFactory().setPoolMaxTotal(100); + + ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs()); + + System.setProperty(BaseClient.HAPI_CLIENT_KEEPRESPONSES, "true"); + } + + @Test + public void testSendMessageAsync() throws Exception { + OperationOutcome oo = new OperationOutcome(); + oo.addIssue().setDiagnostics("FOOBAR"); + final String msg = ourCtx.newJsonParser().encodeResourceToString(oo); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.196:83/fhirServer"); + + client.setEncoding(EncodingEnum.JSON); + +// Create the input message to pass to the server + final Bundle msgBundle = getMessageBundle( + "myEvent", "Test Event", + "MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/"); + +// Invoke $process-message + Object response = client + .operation() + .onServer() + .processMessage() + .setAsyncProcessingMode() + .setResponseUrlParam("http://myserver/fhir") + .setMessageBundle(msgBundle) + .execute(); + + //System.out.println(response); + assertEquals("http://192.168.4.196:83/fhirServer/$process-message?_format=json&async=true&response-url=http%3A%2F%2Fmyserver%2Ffhir", capt.getAllValues().get(0).getURI().toASCIIString()); + assertEquals("POST", capt.getAllValues().get(0).getRequestLine().getMethod()); + //assertEquals("", extractBody(capt, 0)); + //assertNotNull(response.getOperationOutcome()); + assertEquals("FOOBAR", ((OperationOutcome) response).getIssueFirstRep().getDiagnosticsElement().getValue()); + + } + + @Test + public void testSendMessage() throws Exception { + final Bundle msgBundleResponse = getMessageBundle( + "myEvent", "Test Event", + "MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/"); + ((MessageHeader) msgBundleResponse.getEntryFirstRep().getResource()).getResponse().setCode(ResponseTypeEnum.OK); + final String msg = ourCtx.newJsonParser().encodeResourceToString(msgBundleResponse); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.196:83/fhirServer"); + + client.setEncoding(EncodingEnum.JSON); + +// Create the input message to pass to the server + final Bundle msgBundle = getMessageBundle( + "myEvent", "Test Event", + "MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/"); + +// Invoke $process-message + Object response = client + .operation() + .onServer() + .processMessage() + .setResponseUrlParam("http://myserver/fhir") + .setMessageBundle(msgBundle) + .execute(); + + //System.out.println(response); + assertEquals("http://192.168.4.196:83/fhirServer/$process-message?_format=json&response-url=http%3A%2F%2Fmyserver%2Ffhir", capt.getAllValues().get(0).getURI().toASCIIString()); + assertEquals("POST", capt.getAllValues().get(0).getRequestLine().getMethod()); + //assertEquals("", extractBody(capt, 0)); + //assertNotNull(response.getOperationOutcome()); + assertEquals("MessageHeader", ((Bundle) response).getEntryFirstRep().getResource().getResourceName()); + + } + + /** + * Criar um FHIR Message Bundle pre-preenchido com os parametros + * + * @param eventCode + * @param eventDisplay + * @param sourceName + * @param sourceEnpoint + * @param destinationName + * @param destinationEndpoint + * @return Message Bundle + */ + public static Bundle getMessageBundle(String eventCode, String eventDisplay, String sourceName, String sourceEnpoint, String destinationName, String destinationEndpoint) { + /* + Init Bundle + */ + Bundle msgBundle = new Bundle(); + msgBundle.getMeta().setLastUpdated(new Date()); + msgBundle.setType(BundleTypeEnum.MESSAGE); //Document Type + msgBundle.setId(UUID.randomUUID().toString()); // Random ID + /* + Init MessageHeader + */ + MessageHeader msh = new MessageHeader(); + msh.setId(UUID.randomUUID().toString()); + msh.setTimestampWithMillisPrecision(new Date()); + msh.getEvent().setSystem("http://mybServer/fhir/events"); + msh.getEvent().setCode(eventCode); + msh.getEvent().setDisplay(eventDisplay); + msh.getSource().setName(sourceName); + msh.getSource().setEndpoint(sourceEnpoint); + msh.getDestinationFirstRep().setName(destinationName); + msh.getDestinationFirstRep().setEndpoint(destinationEndpoint); + Bundle.Entry entry = new Bundle.Entry(); + entry.setFullUrl("http://mybase/fhirServer/Bundle/" + msh.getId().getValue()); + entry.setResource(msh); + msgBundle.addEntry(entry); + return msgBundle; + } +} From 6eee5e696e9586fdbbbf8c7ad215df78898e01fe Mon Sep 17 00:00:00 2001 From: Hugo Soares Date: Thu, 9 Feb 2017 13:59:00 +0000 Subject: [PATCH 2/3] Changes requested by james https://github.com/jamesagnew/hapi-fhir/pull/542 --- .../ca/uhn/fhir/rest/client/BaseClient.java | 24 - .../uhn/fhir/rest/client/GenericClient.java | 4990 +++++++++-------- .../fhir/rest/client/api/IRestfulClient.java | 16 - .../ca/uhn/fhir/rest/gclient/IOperation.java | 41 +- .../rest/gclient/IOperationProcessMsg.java | 24 +- .../gclient/IOperationProcessMsgMode.java | 34 + .../fhir/rest/gclient/IOperationUnnamed.java | 2 - .../rest/method/OperationMethodBinding.java | 629 +-- hapi-fhir-structures-dstu2/pom.xml | 2 +- .../rest/client/GenericClientDstu2Test.java | 10 - .../rest/client/MessageClientDstu2Test.java | 35 +- 11 files changed, 2920 insertions(+), 2887 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsgMode.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java index b5103972e5e..0cb93da1a37 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java @@ -76,8 +76,6 @@ public abstract class BaseClient implements IRestfulClient { private Boolean myPrettyPrint = false; private SummaryEnum mySummary; private final String myUrlBase; - private Boolean myAsync = null; - private String myResponseUrl = null; BaseClient(IHttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) { super(); @@ -219,17 +217,6 @@ public abstract class BaseClient implements IRestfulClient { if (theSubsetElements != null && theSubsetElements.isEmpty() == false) { params.put(Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ','))); } - - /* - Message Client Params - */ - if(myAsync != null && myAsync == true ) { - params.put(Constants.PARAM_ASYNC, Collections.singletonList(myAsync.toString())); - } - - if(myResponseUrl != null && !myResponseUrl.isEmpty() ) { - params.put(Constants.PARAM_RESPONSE_URL, Collections.singletonList(myResponseUrl)); - } EncodingEnum encoding = getEncoding(); if (theEncoding != null) { @@ -435,7 +422,6 @@ public abstract class BaseClient implements IRestfulClient { public void setSummary(SummaryEnum theSummary) { mySummary = theSummary; } - @Override public void unregisterInterceptor(IClientInterceptor theInterceptor) { @@ -451,16 +437,6 @@ public abstract class BaseClient implements IRestfulClient { return preferResponseTypes; } - @Override - public void setMessageAsync(Boolean isAsync) { - myAsync = isAsync; - } - - @Override - public void setMessageResponseUrl(String responseUrl) { - myResponseUrl = responseUrl; - } - protected final class ResourceResponseHandler implements IClientResponseHandler { private boolean myAllowHtmlResponse; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index 90a5eb62495..94f09a42eeb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -135,2515 +135,2535 @@ import ca.uhn.fhir.util.UrlUtil; */ public class GenericClient extends BaseClient implements IGenericClient { - private static final String I18N_CANNOT_DETEMINE_RESOURCE_TYPE = "ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri"; - private static final String I18N_INCOMPLETE_URI_FOR_READ = "ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead"; - private static final String I18N_NO_VERSION_ID_FOR_VREAD = "ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread"; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class); - private FhirContext myContext; - private IHttpRequest myLastRequest; - private boolean myLogRequestAndResponse; - - /** - * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! - */ - public GenericClient(FhirContext theContext, IHttpClient theHttpClient, String theServerBase, RestfulClientFactory theFactory) { - super(theHttpClient, theServerBase, theFactory); - myContext = theContext; - } - - @Override - public IBaseConformance conformance() { - if (myContext.getVersion().getVersion().isRi()) { - throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/STU3+ structures"); - } - - HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - @SuppressWarnings("unchecked") - Class conformance = (Class) myContext.getResourceDefinition("Conformance").getImplementingClass(); - - ResourceResponseHandler binding = new ResourceResponseHandler(conformance); - IBaseConformance resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public ICreate create() { - return new CreateInternal(); - } - - @Override - public MethodOutcome create(IBaseResource theResource) { - BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(theResource, myContext); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - - } - - @Override - public IDelete delete() { - return new DeleteInternal(); - } - - @Override - public MethodOutcome delete(final Class theType, IdDt theId) { - HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), theId.withResourceType(toResourceName(theType))); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - final String resourceName = myContext.getResourceDefinition(theType).getName(); - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public MethodOutcome delete(Class theType, String theId) { - return delete(theType, new IdDt(theId)); - } - - private T doReadOrVRead(final Class theType, IIdType theId, boolean theVRead, ICallable theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint, - SummaryEnum theSummary, EncodingEnum theEncoding, Set theSubsetElements) { - String resName = toResourceName(theType); - IIdType id = theId; - if (!id.hasBaseUrl()) { - id = new IdDt(resName, id.getIdPart(), id.getVersionIdPart()); - } - - HttpGetClientInvocation invocation; - if (id.hasBaseUrl()) { - if (theVRead) { - invocation = ReadMethodBinding.createAbsoluteVReadInvocation(getFhirContext(), id); - } else { - invocation = ReadMethodBinding.createAbsoluteReadInvocation(getFhirContext(), id); - } - } else { - if (theVRead) { - invocation = ReadMethodBinding.createVReadInvocation(getFhirContext(), id, resName); - } else { - invocation = ReadMethodBinding.createReadInvocation(getFhirContext(), id, resName); - } - } - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - if (theIfVersionMatches != null) { - invocation.addHeader(Constants.HEADER_IF_NONE_MATCH, '"' + theIfVersionMatches + '"'); - } - - boolean allowHtmlResponse = (theSummary == SummaryEnum.TEXT) || (theSummary == null && getSummary() == SummaryEnum.TEXT); - ResourceResponseHandler binding = new ResourceResponseHandler(theType, (Class) null, id, allowHtmlResponse); - - if (theNotModifiedHandler == null) { - return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); - } else { - try { - return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); - } catch (NotModifiedException e) { - return theNotModifiedHandler.call(); - } - } - - } - - @Override - public IFetchConformanceUntyped fetchConformance() { - return new FetchConformanceInternal(); - } - - // public IResource read(UriDt url) { - // return read(inferResourceClass(url), url); - // } - // - // @SuppressWarnings("unchecked") - // public T read(final Class theType, UriDt url) { - // return (T) invoke(theType, url, new ResourceResponseHandler(theType)); - // } - // - // public Bundle search(UriDt url) { - // return search(inferResourceClass(url), url); - // } - - @Override - public void forceConformanceCheck() { - super.forceConformanceCheck(); - } - - @Override - public FhirContext getFhirContext() { - return myContext; - } - - public IHttpRequest getLastRequest() { - return myLastRequest; - } - - protected String getPreferredId(IBaseResource theResource, String theId) { - if (isNotBlank(theId)) { - return theId; - } - return theResource.getIdElement().getIdPart(); - } - - @Override - public IGetTags getTags() { - return new GetTagsInternal(); - } - - @Override - public IHistory history() { - return new HistoryInternal(); - } - - @Override - public Bundle history(final Class theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit) { - String resourceName = theType != null ? toResourceName(theType) : null; - String id = theIdDt != null && theIdDt.isEmpty() == false ? theIdDt.getValue() : null; - HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, theSince, theLimit); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - BundleResponseHandler binding = new BundleResponseHandler(theType); - Bundle resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - - } - - @Override - public Bundle history(Class theType, String theId, DateTimeDt theSince, Integer theLimit) { - return history(theType, new IdDt(theId), theSince, theLimit); - } - - private Class inferResourceClass(UriDt theUrl) { - String urlString = theUrl.getValueAsString(); - int i = urlString.indexOf('?'); - - if (i >= 0) { - urlString = urlString.substring(0, i); - } - - i = urlString.indexOf("://"); - - if (i >= 0) { - urlString = urlString.substring(i + 3); - } - - String[] pcs = urlString.split("\\/"); - - for (i = pcs.length - 1; i >= 0; i--) { - String s = pcs[i].trim(); - - if (!s.isEmpty()) { - RuntimeResourceDefinition def = myContext.getResourceDefinition(s); - if (def != null) { - return def.getImplementingClass(); - } - } - } - - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); - - } - - // @Override - // public T read(final Class theType, IdDt theId) { - // return doReadOrVRead(theType, theId, false, null, null); - // } - - /** - * @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your - * client instead, as this provides much more fine-grained control over what is logged. This - * method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16) - */ - @Deprecated - public boolean isLogRequestAndResponse() { - return myLogRequestAndResponse; - } - - @Override - public IGetPage loadPage() { - return new LoadPageInternal(); - } - - @Override - public IMeta meta() { - if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { - throw new IllegalStateException("Can not call $meta operations on a DSTU1 client"); - } - return new MetaInternal(); - } - - @Override - public IOperation operation() { - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) == false) { - 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()); - } - return new OperationInternal(); - } - - @Override - public IRead read() { - return new ReadInternal(); - } - - @Override - public T read(Class theType, String theId) { - return read(theType, new IdDt(theId)); - } - - @Override - public T read(final Class theType, UriDt theUrl) { - IdDt id = theUrl instanceof IdDt ? ((IdDt) theUrl) : new IdDt(theUrl); - return doReadOrVRead(theType, id, false, null, null, false, null, null, null); - } - - @Override - public IBaseResource read(UriDt theUrl) { - IdDt id = new IdDt(theUrl); - String resourceType = id.getResourceType(); - if (isBlank(resourceType)) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, theUrl.getValueAsString())); - } - RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType); - if (def == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); - } - return read(def.getImplementingClass(), id); - } - - @Override - public IUntypedQuery search() { - return new SearchInternal(); - } - - @Override - public Bundle search(final Class theType, Map> theParams) { - LinkedHashMap> params = new LinkedHashMap>(); - for (Entry> nextEntry : theParams.entrySet()) { - ArrayList valueList = new ArrayList(); - String qualifier = null; - for (IQueryParameterType nextValue : nextEntry.getValue()) { - valueList.add(nextValue.getValueAsQueryToken(myContext)); - qualifier = nextValue.getQueryParameterQualifier(); - } - qualifier = StringUtils.defaultString(qualifier); - params.put(nextEntry.getKey() + qualifier, valueList); - } - - BaseHttpClientInvocation invocation = SearchMethodBinding.createSearchInvocation(myContext, toResourceName(theType), params, null, null, null); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - BundleResponseHandler binding = new BundleResponseHandler(theType); - Bundle resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public Bundle search(final Class theType, UriDt theUrl) { - BaseHttpClientInvocation invocation = new HttpGetClientInvocation(getFhirContext(), theUrl.getValueAsString()); - return invokeClient(myContext, new BundleResponseHandler(theType), invocation); - } - - @Override - public Bundle search(UriDt theUrl) { - return search(inferResourceClass(theUrl), theUrl); - } - - /** - * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! - */ - public void setLastRequest(IHttpRequest theLastRequest) { - myLastRequest = theLastRequest; - } - - @Override - public void setLogRequestAndResponse(boolean theLogRequestAndResponse) { - myLogRequestAndResponse = theLogRequestAndResponse; - } - - private String toResourceName(Class theType) { - return myContext.getResourceDefinition(theType).getName(); - } - - @Override - public ITransaction transaction() { - return new TransactionInternal(); - } - - @Override - public List transaction(List theResources) { - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - Bundle resp = invokeClient(myContext, new BundleResponseHandler(null), invocation, myLogRequestAndResponse); - - return new ArrayList(resp.toListOfResources()); - } - - @Override - public IPatch patch() { - return new PatchInternal(); - } - - @Override - public IUpdate update() { - return new UpdateInternal(); - } - - @Override - public MethodOutcome update(IdDt theIdDt, IBaseResource theResource) { - BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public MethodOutcome update(String theId, IBaseResource theResource) { - return update(new IdDt(theId), theResource); - } - - @Override - public IValidate validate() { - return new ValidateInternal(); - } - - @Override - public MethodOutcome validate(IBaseResource theResource) { - BaseHttpClientInvocation invocation; - if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { - invocation = ValidateMethodBindingDstu1.createValidateInvocation(theResource, null, myContext); - } else { - invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, theResource); - } - - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public T vread(final Class theType, IdDt theId) { - if (theId.hasVersionIdPart() == false) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue())); - } - return doReadOrVRead(theType, theId, true, null, null, false, null, null, null); - } - - /* also deprecated in interface */ - @Deprecated - @Override - public T vread(final Class theType, IdDt theId, IdDt theVersionId) { - return vread(theType, theId.withVersion(theVersionId.getIdPart())); - } - - @Override - public T vread(Class theType, String theId, String theVersionId) { - IdDt resId = new IdDt(toResourceName(theType), theId, theVersionId); - return vread(theType, resId); - } - - private static void addParam(Map> params, String parameterName, String parameterValue) { - if (!params.containsKey(parameterName)) { - params.put(parameterName, new ArrayList()); - } - params.get(parameterName).add(parameterValue); - } - - private static void addPreferHeader(PreferReturnEnum thePrefer, BaseHttpClientInvocation theInvocation) { - if (thePrefer != null) { - theInvocation.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + thePrefer.getHeaderValue()); - } - } - - private static String validateAndEscapeConditionalUrl(String theSearchUrl) { - Validate.notBlank(theSearchUrl, "Conditional URL can not be blank/null"); - StringBuilder b = new StringBuilder(); - boolean haveHadQuestionMark = false; - for (int i = 0; i < theSearchUrl.length(); i++) { - char nextChar = theSearchUrl.charAt(i); - if (!haveHadQuestionMark) { - if (nextChar == '?') { - haveHadQuestionMark = true; - } else if (!Character.isLetter(nextChar)) { - throw new IllegalArgumentException("Conditional URL must be in the format \"[ResourceType]?[Params]\" and must not have a base URL - Found: " + theSearchUrl); - } - b.append(nextChar); - } else { - switch (nextChar) { - case '|': - case '?': - case '$': - case ':': - b.append(UrlUtil.escape(Character.toString(nextChar))); - break; - default: - b.append(nextChar); - break; - } - } - } - return b.toString(); - } - - private abstract class BaseClientExecutable, Y> implements IClientExecutable { - - protected EncodingEnum myParamEncoding; - - private List> myPreferResponseTypes; - - protected Boolean myPrettyPrint; - - private boolean myQueryLogRequestAndResponse; - - private HashSet mySubsetElements; - - protected SummaryEnum mySummaryMode; - - @SuppressWarnings("unchecked") - @Override - public T andLogRequestAndResponse(boolean theLogRequestAndResponse) { - myQueryLogRequestAndResponse = theLogRequestAndResponse; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T elementsSubset(String... theElements) { - if (theElements != null && theElements.length > 0) { - mySubsetElements = new HashSet(Arrays.asList(theElements)); - } else { - mySubsetElements = null; - } - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T encodedJson() { - myParamEncoding = EncodingEnum.JSON; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T encodedXml() { - myParamEncoding = EncodingEnum.XML; - return (T) this; - } - - protected EncodingEnum getParamEncoding() { - return myParamEncoding; - } - - public List> getPreferResponseTypes() { - return myPreferResponseTypes; - } - - public List> getPreferResponseTypes(Class theDefault) { - if (myPreferResponseTypes != null) { - return myPreferResponseTypes; - } else { - return toTypeList(theDefault); - } - } - - protected HashSet getSubsetElements() { - return mySubsetElements; - } - - protected Z invoke(Map> theParams, IClientResponseHandler theHandler, BaseHttpClientInvocation theInvocation) { - // if (myParamEncoding != null) { - // theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType())); - // } - // - // if (myPrettyPrint != null) { - // theParams.put(Constants.PARAM_PRETTY, Collections.singletonList(myPrettyPrint.toString())); - // } - - if (isKeepResponses()) { - myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint); - } - - Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse, mySummaryMode, mySubsetElements); - return resp; - } - - protected IBaseResource parseResourceBody(String theResourceBody) { - EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody); - if (encoding == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); - } - return encoding.newParser(myContext).parseResource(theResourceBody); - } - - @SuppressWarnings("unchecked") - @Override - public T preferResponseType(Class theClass) { - myPreferResponseTypes = null; - if (theClass != null) { - myPreferResponseTypes = new ArrayList>(); - myPreferResponseTypes.add(theClass); - } - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T preferResponseTypes(List> theClass) { - myPreferResponseTypes = theClass; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T prettyPrint() { - myPrettyPrint = true; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T summaryMode(SummaryEnum theSummary) { - mySummaryMode = theSummary; - return ((T) this); - } - - } - - private final class BundleResponseHandler implements IClientResponseHandler { - - private Class myType; - - public BundleResponseHandler(Class theType) { - myType = theType; - } - - @Override - public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); - } - IParser parser = respType.newParser(myContext); - return parser.parseBundle(myType, theResponseReader); - } - } - - private class CreateInternal extends BaseClientExecutable implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped { - - private CriterionList myCriterionList; - private String myId; - private PreferReturnEnum myPrefer; - private IBaseResource myResource; - private String myResourceBody; - private String mySearchUrl; - - @Override - public ICreateWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public ICreateWithQuery conditional() { - myCriterionList = new CriterionList(); - return this; - } - - @Override - public ICreateTyped conditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public MethodOutcome execute() { - if (myResource == null) { - myResource = parseResourceBody(myResourceBody); - } - myId = getPreferredId(myResource, myId); - - // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding - if (getParamEncoding() != null) { - myResourceBody = null; - } - - BaseHttpClientInvocation invocation; - if (mySearchUrl != null) { - invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, mySearchUrl); - } else if (myCriterionList != null) { - invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, myCriterionList.toParamList()); - } else { - invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext); - } - - addPreferHeader(myPrefer, invocation); - - RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer); - - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - - } - - @Override - public ICreateTyped prefer(PreferReturnEnum theReturn) { - myPrefer = theReturn; - return this; - } - - @Override - public ICreateTyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "Resource can not be null"); - myResource = theResource; - return this; - } - - @Override - public ICreateTyped resource(String theResourceBody) { - Validate.notBlank(theResourceBody, "Body can not be null or blank"); - myResourceBody = theResourceBody; - return this; - } - - @Override - public ICreateWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public CreateInternal withId(IdDt theId) { - myId = theId.getIdPart(); - return this; - } - - @Override - public CreateInternal withId(String theId) { - myId = theId; - return this; - } - - } - - private class CriterionList extends ArrayList { - - private static final long serialVersionUID = 1L; - - public void populateParamList(Map> theParams) { - for (ICriterionInternal next : this) { - String parameterName = next.getParameterName(); - String parameterValue = next.getParameterValue(myContext); - if (isNotBlank(parameterValue)) { - addParam(theParams, parameterName, parameterValue); - } - } - } - - public Map> toParamList() { - LinkedHashMap> retVal = new LinkedHashMap>(); - populateParamList(retVal); - return retVal; - } - - } - - private class DeleteInternal extends BaseClientExecutable implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped { - - private CriterionList myCriterionList; - private IIdType myId; - private String myResourceType; - private String mySearchUrl; - - @Override - public IDeleteWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IBaseOperationOutcome execute() { - HttpDeleteClientInvocation invocation; - if (myId != null) { - invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myId); - } else if (myCriterionList != null) { - Map> params = myCriterionList.toParamList(); - invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myResourceType, params); - } else { - invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), mySearchUrl); - } - OperationOutcomeResponseHandler binding = new OperationOutcomeResponseHandler(); - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - } - - @Override - public IDeleteTyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "theResource can not be null"); - IIdType id = theResource.getIdElement(); - Validate.notNull(id, "theResource.getIdElement() can not be null"); - if (id.hasResourceType() == false || id.hasIdPart() == false) { - throw new IllegalArgumentException("theResource.getId() must contain a resource type and logical ID at a minimum (e.g. Patient/1234), found: " + id.getValue()); - } - myId = id; - return this; - } - - @Override - public IDeleteTyped resourceById(IIdType theId) { - Validate.notNull(theId, "theId can not be null"); - if (theId.hasResourceType() == false || theId.hasIdPart() == false) { - throw new IllegalArgumentException("theId must contain a resource type and logical ID at a minimum (e.g. Patient/1234)found: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IDeleteTyped resourceById(String theResourceType, String theLogicalId) { - Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); - if (myContext.getResourceDefinition(theResourceType) == null) { - throw new IllegalArgumentException("Unknown resource type"); - } - Validate.notBlank(theLogicalId, "theLogicalId can not be blank/null"); - if (theLogicalId.contains("/")) { - throw new IllegalArgumentException("LogicalId can not contain '/' (should only be the logical ID portion, not a qualified ID)"); - } - myId = new IdDt(theResourceType, theLogicalId); - return this; - } - - @Override - public IDeleteWithQuery resourceConditionalByType(Class theResourceType) { - Validate.notNull(theResourceType, "theResourceType can not be null"); - myCriterionList = new CriterionList(); - myResourceType = myContext.getResourceDefinition(theResourceType).getName(); - return this; - } - - @Override - public IDeleteWithQuery resourceConditionalByType(String theResourceType) { - Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); - if (myContext.getResourceDefinition(theResourceType) == null) { - throw new IllegalArgumentException("Unknown resource type: " + theResourceType); - } - myResourceType = theResourceType; - myCriterionList = new CriterionList(); - return this; - } - - @Override - public IDeleteTyped resourceConditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public IDeleteWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private class FetchConformanceInternal extends BaseClientExecutable implements IFetchConformanceUntyped, IFetchConformanceTyped { - private RuntimeResourceDefinition myType; - - @Override - public Object execute() { - ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass()); - HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); - return super.invoke(null, binding, invocation); - } - - @Override - public IFetchConformanceTyped ofType(Class theResourceType) { - Validate.notNull(theResourceType, "theResourceType must not be null"); - myType = myContext.getResourceDefinition(theResourceType); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); - } - return this; - } - - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private class GetPageInternal extends BaseClientExecutable, Object> implements IGetPageTyped { - - private Class myBundleType; - private String myUrl; - - public GetPageInternal(String theUrl) { - myUrl = theUrl; - } - - public GetPageInternal(String theUrl, Class theBundleType) { - myUrl = theUrl; - myBundleType = theBundleType; - } - - @Override - public Object execute() { - IClientResponseHandler binding; - if (myBundleType == null) { - binding = new BundleResponseHandler(null); - } else { - binding = new ResourceResponseHandler(myBundleType, getPreferResponseTypes()); - } - HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myContext, myUrl); - - Map> params = null; - return invoke(params, binding, invocation); - } - - } - - private class GetTagsInternal extends BaseClientExecutable implements IGetTags { - - private String myId; - private String myResourceName; - private String myVersionId; - - @Override - public TagList execute() { - - Map> params = new LinkedHashMap>(); - Map> initial = createExtraParams(); - if (initial != null) { - params.putAll(initial); - } - - TagListResponseHandler binding = new TagListResponseHandler(); - List urlFragments = new ArrayList(); - if (isNotBlank(myResourceName)) { - urlFragments.add(myResourceName); - if (isNotBlank(myId)) { - urlFragments.add(myId); - if (isNotBlank(myVersionId)) { - urlFragments.add(Constants.PARAM_HISTORY); - urlFragments.add(myVersionId); - } - } - } - urlFragments.add(Constants.PARAM_TAGS); - - HttpGetClientInvocation invocation = new HttpGetClientInvocation(myContext, params, urlFragments); - - return invoke(params, binding, invocation); - - } - - @Override - public IGetTags forResource(Class theClass) { - setResourceClass(theClass); - return this; - } - - @Override - public IGetTags forResource(Class theClass, String theId) { - setResourceClass(theClass); - myId = theId; - return this; - } - - @Override - public IGetTags forResource(Class theClass, String theId, String theVersionId) { - setResourceClass(theClass); - myId = theId; - myVersionId = theVersionId; - return this; - } - - private void setResourceClass(Class theClass) { - if (theClass != null) { - myResourceName = myContext.getResourceDefinition(theClass).getName(); - } else { - myResourceName = null; - } - } - - } - - @SuppressWarnings("rawtypes") - private class HistoryInternal extends BaseClientExecutable implements IHistory, IHistoryUntyped, IHistoryTyped { - - private Integer myCount; - private IIdType myId; - private Class myReturnType; - private IPrimitiveType mySince; - private Class myType; - - @SuppressWarnings("unchecked") - @Override - public IHistoryTyped andReturnBundle(Class theType) { - myReturnType = theType; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IHistoryTyped andReturnDstu1Bundle() { - return this; - } - - @Override - public IHistoryTyped count(Integer theCount) { - myCount = theCount; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public Object execute() { - String resourceName; - String id; - if (myType != null) { - resourceName = myContext.getResourceDefinition(myType).getName(); - id = null; - } else if (myId != null) { - resourceName = myId.getResourceType(); - id = myId.getIdPart(); - } else { - resourceName = null; - id = null; - } - - HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, mySince, myCount); - - IClientResponseHandler handler; - if (myReturnType != null) { - handler = new ResourceResponseHandler(myReturnType, getPreferResponseTypes(myType)); - } else { - handler = new BundleResponseHandler(null); - } - - return invoke(null, handler, invocation); - } - - @Override - public IHistoryUntyped onInstance(IIdType theId) { - if (theId.hasResourceType() == false) { - throw new IllegalArgumentException("Resource ID does not have a resource type: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IHistoryUntyped onServer() { - return this; - } - - @Override - public IHistoryUntyped onType(Class theResourceType) { - myType = theResourceType; - return this; - } - - @Override - public IHistoryTyped since(Date theCutoff) { - if (theCutoff != null) { - mySince = new InstantDt(theCutoff); - } else { - mySince = null; - } - return this; - } - - @Override - public IHistoryTyped since(IPrimitiveType theCutoff) { - mySince = theCutoff; - return this; - } - - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private final class LoadPageInternal implements IGetPage, IGetPageUntyped { - - private static final String PREV = "prev"; - private static final String PREVIOUS = "previous"; - private String myPageUrl; - - @Override - public IGetPageTyped andReturnBundle(Class theBundleType) { - Validate.notNull(theBundleType, "theBundleType must not be null"); - return new GetPageInternal(myPageUrl, theBundleType); - } - - @Override - public IGetPageTyped andReturnDstu1Bundle() { - return new GetPageInternal(myPageUrl); - } - - @Override - public IGetPageUntyped byUrl(String thePageUrl) { - if (isBlank(thePageUrl)) { - throw new IllegalArgumentException("thePagingUrl must not be blank or null"); - } - myPageUrl = thePageUrl; - return this; - } - - @Override - public IGetPageTyped next(Bundle theBundle) { - return new GetPageInternal(theBundle.getLinkNext().getValue()); - } - - @Override - public IGetPageTyped next(T theBundle) { - return nextOrPrevious("next", theBundle); - } - - private IGetPageTyped nextOrPrevious(String theWantRel, T theBundle) { - RuntimeResourceDefinition def = myContext.getResourceDefinition(theBundle); - List links = def.getChildByName("link").getAccessor().getValues(theBundle); - if (links == null || links.isEmpty()) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); - } - for (IBase nextLink : links) { - BaseRuntimeElementCompositeDefinition linkDef = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(nextLink.getClass()); - List rel = linkDef.getChildByName("relation").getAccessor().getValues(nextLink); - if (rel == null || rel.isEmpty()) { - continue; - } - String relation = ((IPrimitiveType) rel.get(0)).getValueAsString(); - if (theWantRel.equals(relation) || (theWantRel == PREVIOUS && PREV.equals(relation))) { - List urls = linkDef.getChildByName("url").getAccessor().getValues(nextLink); - if (urls == null || urls.isEmpty()) { - continue; - } - String url = ((IPrimitiveType) urls.get(0)).getValueAsString(); - if (isBlank(url)) { - continue; - } - return (IGetPageTyped) byUrl(url).andReturnBundle(theBundle.getClass()); - } - } - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); - } - - @Override - public IGetPageTyped previous(Bundle theBundle) { - return new GetPageInternal(theBundle.getLinkPrevious().getValue()); - } - - @Override - public IGetPageTyped previous(T theBundle) { - return nextOrPrevious(PREVIOUS, theBundle); - } - - @Override - public IGetPageTyped url(String thePageUrl) { - return new GetPageInternal(thePageUrl); - } - - } - - @SuppressWarnings("rawtypes") - private class MetaInternal extends BaseClientExecutable implements IMeta, IMetaAddOrDeleteUnsourced, IMetaGetUnsourced, IMetaAddOrDeleteSourced { - - private IIdType myId; - private IBaseMetaType myMeta; - private Class myMetaType; - private String myOnType; - private MetaOperation myOperation; - - @Override - public IMetaAddOrDeleteUnsourced add() { - myOperation = MetaOperation.ADD; - return this; - } - - @Override - public IMetaAddOrDeleteUnsourced delete() { - myOperation = MetaOperation.DELETE; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public Object execute() { - - BaseHttpClientInvocation invocation = null; - - IBaseParameters parameters = ParametersUtil.newInstance(myContext); - switch (myOperation) { - case ADD: - ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta"); - invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-add", parameters, false); - break; - case DELETE: - ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta"); - invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-delete", parameters, false); - break; - case GET: - if (myId != null) { - invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, myId.getIdPart(), "$meta", parameters, true); - } else if (myOnType != null) { - invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, null, "$meta", parameters, true); - } else { - invocation = OperationMethodBinding.createOperationInvocation(myContext, null, null, "$meta", parameters, true); - } - break; - } - - // Should not happen - if (invocation == null) { - throw new IllegalStateException(); - } - - IClientResponseHandler handler; - handler = new MetaParametersResponseHandler(myMetaType); - return invoke(null, handler, invocation); - } - - @Override - public IClientExecutable fromResource(IIdType theId) { - setIdInternal(theId); - return this; - } - - @Override - public IClientExecutable fromServer() { - return this; - } - - @Override - public IClientExecutable fromType(String theResourceName) { - Validate.notBlank(theResourceName, "theResourceName must not be blank"); - myOnType = theResourceName; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IMetaGetUnsourced get(Class theType) { - myMetaType = theType; - myOperation = MetaOperation.GET; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IClientExecutable, T> meta(T theMeta) { - Validate.notNull(theMeta, "theMeta must not be null"); - myMeta = theMeta; - myMetaType = myMeta.getClass(); - return this; - } - - @Override - public IMetaAddOrDeleteSourced onResource(IIdType theId) { - setIdInternal(theId); - return this; - } - - private void setIdInternal(IIdType theId) { - Validate.notBlank(theId.getResourceType(), "theId must contain a resource type"); - Validate.notBlank(theId.getIdPart(), "theId must contain an ID part"); - myOnType = theId.getResourceType(); - myId = theId; - } - - } - - private enum MetaOperation { - ADD, DELETE, GET - } - - private final class MetaParametersResponseHandler implements IClientResponseHandler { - - private Class myType; - - public MetaParametersResponseHandler(Class theMetaType) { - myType = theMetaType; - } - - @SuppressWarnings("unchecked") - @Override - public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); - } - IParser parser = respType.newParser(myContext); - RuntimeResourceDefinition type = myContext.getResourceDefinition("Parameters"); - IBaseResource retVal = parser.parseResource(type.getImplementingClass(), theResponseReader); - - BaseRuntimeChildDefinition paramChild = type.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); - List parameter = paramChild.getAccessor().getValues(retVal); - if (parameter == null || parameter.isEmpty()) { - return (T) myContext.getElementDefinition(myType).newInstance(); - } - IBase param = parameter.get(0); - - List meta = paramChildElem.getChildByName("value[x]").getAccessor().getValues(param); - if (meta.isEmpty()) { - return (T) myContext.getElementDefinition(myType).newInstance(); - } - return (T) meta.get(0); - - } - } - - @SuppressWarnings("rawtypes") - private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput, IOperationUntypedWithInputAndPartialOutput, IOperationProcessMsg { - - private IIdType myId; - private String myOperationName; - private IBaseParameters myParameters; - private RuntimeResourceDefinition myParametersDef; - private Class myType; - private boolean myUseHttpGet; - private IBaseBundle myMsgBundle; - private Class myReturnResourceType; - - - @SuppressWarnings("unchecked") - private void addParam(String theName, IBase theValue) { - BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition parameterElem = (BaseRuntimeElementCompositeDefinition) parameterChild.getChildByName("parameter"); - - IBase parameter = parameterElem.newInstance(); - parameterChild.getMutator().addValue(myParameters, parameter); - - IPrimitiveType name = (IPrimitiveType) myContext.getElementDefinition("string").newInstance(); - name.setValue(theName); - parameterElem.getChildByName("name").getMutator().setValue(parameter, name); - - if (theValue instanceof IBaseDatatype) { - BaseRuntimeElementDefinition datatypeDef = myContext.getElementDefinition(theValue.getClass()); - if (datatypeDef instanceof IRuntimeDatatypeDefinition) { - Class profileOf = ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf(); - if (profileOf != null) { - datatypeDef = myContext.getElementDefinition(profileOf); - } - } - String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName()); - BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName); - childByName.getMutator().setValue(parameter, theValue); - } else if (theValue instanceof IBaseResource) { - parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue); - } else { - throw new IllegalArgumentException("Don't know how to handle parameter of type " + theValue.getClass()); - } - } - - private void addParam(String theName, IQueryParameterType theValue) { - IPrimitiveType stringType = ParametersUtil.createString(myContext, theValue.getValueAsQueryToken(myContext)); - addParam(theName, stringType); - } - - @Override - public IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue) { - Validate.notEmpty(theName, "theName must not be null"); - Validate.notNull(theValue, "theValue must not be null"); - addParam(theName, theValue); - return this; - } - - @Override - public IOperationUntypedWithInputAndPartialOutput andSearchParameter(String theName, IQueryParameterType theValue) { - addParam(theName, theValue); - - return this; - } - - @SuppressWarnings("unchecked") - @Override - public Object execute() { - - if (myOperationName != null && myOperationName.equals(Constants.EXTOP_PROCESS_MESSAGE)) { - //If is $process-message operation - BaseHttpClientInvocation invocation = OperationMethodBinding.createProcessMsgInvocation(myContext, myOperationName, myMsgBundle); - - ResourceResponseHandler handler = new ResourceResponseHandler(); - handler.setPreferResponseTypes(getPreferResponseTypes(myType)); - - /* - Map> urlParams = new LinkedHashMap>(); - GenericClient.this.addParam(urlParams, Constants.PARAM_ASYNC, String.valueOf(myAsync)); - GenericClient.this.addParam(urlParams, Constants.PARAM_RESPONSE_URL, String.valueOf(myRespondToUri)); - */ - - Object retVal = invoke(null, handler, invocation); - return retVal; - } else { - String resourceName; - String id; - if (myType != null) { - resourceName = myContext.getResourceDefinition(myType).getName(); - id = null; - } else if (myId != null) { - resourceName = myId.getResourceType(); - id = myId.getIdPart(); - } else { - resourceName = null; - id = null; - } - - BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet); - - ResourceResponseHandler handler = new ResourceResponseHandler(); - handler.setPreferResponseTypes(getPreferResponseTypes(myType)); - - Object retVal = invoke(null, handler, invocation); - if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { - return retVal; - } else { - RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); - IBaseResource parameters = def.newInstance(); - - BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); - IBase parameter = paramChildElem.newInstance(); - paramChild.getMutator().addValue(parameters, parameter); - - BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); - resourceElem.getMutator().addValue(parameter, (IBase) retVal); - - return parameters; - } - } - - } - - @Override - public IOperationUntyped named(String theName) { - Validate.notBlank(theName, "theName can not be null"); - myOperationName = theName; - return this; - } - - @Override - public IOperationProcessMsg processMessage() { - myOperationName = Constants.EXTOP_PROCESS_MESSAGE; - return this; + private static final String I18N_CANNOT_DETEMINE_RESOURCE_TYPE = "ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri"; + private static final String I18N_INCOMPLETE_URI_FOR_READ = "ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead"; + private static final String I18N_NO_VERSION_ID_FOR_VREAD = "ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class); + private FhirContext myContext; + private IHttpRequest myLastRequest; + private boolean myLogRequestAndResponse; + + /** + * For now, this is a part of the internal API of HAPI - Use with caution as + * this method may change! + */ + public GenericClient(FhirContext theContext, IHttpClient theHttpClient, String theServerBase, RestfulClientFactory theFactory) { + super(theHttpClient, theServerBase, theFactory); + myContext = theContext; + } + + @Override + public IBaseConformance conformance() { + if (myContext.getVersion().getVersion().isRi()) { + throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/STU3+ structures"); + } + + HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + @SuppressWarnings("unchecked") + Class conformance = (Class) myContext.getResourceDefinition("Conformance").getImplementingClass(); + + ResourceResponseHandler binding = new ResourceResponseHandler(conformance); + IBaseConformance resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); + return resp; + } + + @Override + public ICreate create() { + return new CreateInternal(); + } + + @Override + public MethodOutcome create(IBaseResource theResource) { + BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(theResource, myContext); + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); + final String resourceName = def.getName(); + + OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); + + MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); + return resp; + + } + + @Override + public IDelete delete() { + return new DeleteInternal(); + } + + @Override + public MethodOutcome delete(final Class theType, IdDt theId) { + HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), theId.withResourceType(toResourceName(theType))); + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + final String resourceName = myContext.getResourceDefinition(theType).getName(); + OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); + MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); + return resp; + } + + @Override + public MethodOutcome delete(Class theType, String theId) { + return delete(theType, new IdDt(theId)); + } + + private T doReadOrVRead(final Class theType, IIdType theId, boolean theVRead, ICallable theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint, + SummaryEnum theSummary, EncodingEnum theEncoding, Set theSubsetElements) { + String resName = toResourceName(theType); + IIdType id = theId; + if (!id.hasBaseUrl()) { + id = new IdDt(resName, id.getIdPart(), id.getVersionIdPart()); + } + + HttpGetClientInvocation invocation; + if (id.hasBaseUrl()) { + if (theVRead) { + invocation = ReadMethodBinding.createAbsoluteVReadInvocation(getFhirContext(), id); + } else { + invocation = ReadMethodBinding.createAbsoluteReadInvocation(getFhirContext(), id); + } + } else { + if (theVRead) { + invocation = ReadMethodBinding.createVReadInvocation(getFhirContext(), id, resName); + } else { + invocation = ReadMethodBinding.createReadInvocation(getFhirContext(), id, resName); + } + } + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + if (theIfVersionMatches != null) { + invocation.addHeader(Constants.HEADER_IF_NONE_MATCH, '"' + theIfVersionMatches + '"'); + } + + boolean allowHtmlResponse = (theSummary == SummaryEnum.TEXT) || (theSummary == null && getSummary() == SummaryEnum.TEXT); + ResourceResponseHandler binding = new ResourceResponseHandler(theType, (Class) null, id, allowHtmlResponse); + + if (theNotModifiedHandler == null) { + return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); + } else { + try { + return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); + } catch (NotModifiedException e) { + return theNotModifiedHandler.call(); + } + } + + } + + @Override + public IFetchConformanceUntyped fetchConformance() { + return new FetchConformanceInternal(); + } + + // public IResource read(UriDt url) { + // return read(inferResourceClass(url), url); + // } + // + // @SuppressWarnings("unchecked") + // public T read(final Class theType, UriDt url) { + // return (T) invoke(theType, url, new ResourceResponseHandler(theType)); + // } + // + // public Bundle search(UriDt url) { + // return search(inferResourceClass(url), url); + // } + @Override + public void forceConformanceCheck() { + super.forceConformanceCheck(); + } + + @Override + public FhirContext getFhirContext() { + return myContext; + } + + public IHttpRequest getLastRequest() { + return myLastRequest; + } + + protected String getPreferredId(IBaseResource theResource, String theId) { + if (isNotBlank(theId)) { + return theId; + } + return theResource.getIdElement().getIdPart(); + } + + @Override + public IGetTags getTags() { + return new GetTagsInternal(); + } + + @Override + public IHistory history() { + return new HistoryInternal(); + } + + @Override + public Bundle history(final Class theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit) { + String resourceName = theType != null ? toResourceName(theType) : null; + String id = theIdDt != null && theIdDt.isEmpty() == false ? theIdDt.getValue() : null; + HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, theSince, theLimit); + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + BundleResponseHandler binding = new BundleResponseHandler(theType); + Bundle resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); + return resp; + + } + + @Override + public Bundle history(Class theType, String theId, DateTimeDt theSince, Integer theLimit) { + return history(theType, new IdDt(theId), theSince, theLimit); + } + + private Class inferResourceClass(UriDt theUrl) { + String urlString = theUrl.getValueAsString(); + int i = urlString.indexOf('?'); + + if (i >= 0) { + urlString = urlString.substring(0, i); + } + + i = urlString.indexOf("://"); + + if (i >= 0) { + urlString = urlString.substring(i + 3); + } + + String[] pcs = urlString.split("\\/"); + + for (i = pcs.length - 1; i >= 0; i--) { + String s = pcs[i].trim(); + + if (!s.isEmpty()) { + RuntimeResourceDefinition def = myContext.getResourceDefinition(s); + if (def != null) { + return def.getImplementingClass(); + } + } + } + + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); + + } + + // @Override + // public T read(final Class theType, IdDt theId) { + // return doReadOrVRead(theType, theId, false, null, null); + // } + /** + * @deprecated Use {@link LoggingInterceptor} as a client interceptor + * registered to your client instead, as this provides much more + * fine-grained control over what is logged. This method will be removed at + * some point (deprecated in HAPI 1.6 - 2016-06-16) + */ + @Deprecated + public boolean isLogRequestAndResponse() { + return myLogRequestAndResponse; + } + + @Override + public IGetPage loadPage() { + return new LoadPageInternal(); + } + + @Override + public IMeta meta() { + if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { + throw new IllegalStateException("Can not call $meta operations on a DSTU1 client"); + } + return new MetaInternal(); + } + + @Override + public IOperation operation() { + if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) == false) { + 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()); + } + return new OperationInternal(); + } + + @Override + public IRead read() { + return new ReadInternal(); + } + + @Override + public T read(Class theType, String theId) { + return read(theType, new IdDt(theId)); + } + + @Override + public T read(final Class theType, UriDt theUrl) { + IdDt id = theUrl instanceof IdDt ? ((IdDt) theUrl) : new IdDt(theUrl); + return doReadOrVRead(theType, id, false, null, null, false, null, null, null); + } + + @Override + public IBaseResource read(UriDt theUrl) { + IdDt id = new IdDt(theUrl); + String resourceType = id.getResourceType(); + if (isBlank(resourceType)) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, theUrl.getValueAsString())); + } + RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType); + if (def == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); + } + return read(def.getImplementingClass(), id); + } + + @Override + public IUntypedQuery search() { + return new SearchInternal(); + } + + @Override + public Bundle search(final Class theType, Map> theParams) { + LinkedHashMap> params = new LinkedHashMap>(); + for (Entry> nextEntry : theParams.entrySet()) { + ArrayList valueList = new ArrayList(); + String qualifier = null; + for (IQueryParameterType nextValue : nextEntry.getValue()) { + valueList.add(nextValue.getValueAsQueryToken(myContext)); + qualifier = nextValue.getQueryParameterQualifier(); + } + qualifier = StringUtils.defaultString(qualifier); + params.put(nextEntry.getKey() + qualifier, valueList); + } + + BaseHttpClientInvocation invocation = SearchMethodBinding.createSearchInvocation(myContext, toResourceName(theType), params, null, null, null); + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + BundleResponseHandler binding = new BundleResponseHandler(theType); + Bundle resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); + return resp; + } + + @Override + public Bundle search(final Class theType, UriDt theUrl) { + BaseHttpClientInvocation invocation = new HttpGetClientInvocation(getFhirContext(), theUrl.getValueAsString()); + return invokeClient(myContext, new BundleResponseHandler(theType), invocation); + } + + @Override + public Bundle search(UriDt theUrl) { + return search(inferResourceClass(theUrl), theUrl); + } + + /** + * For now, this is a part of the internal API of HAPI - Use with caution as + * this method may change! + */ + public void setLastRequest(IHttpRequest theLastRequest) { + myLastRequest = theLastRequest; + } + + @Override + public void setLogRequestAndResponse(boolean theLogRequestAndResponse) { + myLogRequestAndResponse = theLogRequestAndResponse; + } + + private String toResourceName(Class theType) { + return myContext.getResourceDefinition(theType).getName(); + } + + @Override + public ITransaction transaction() { + return new TransactionInternal(); + } + + @Override + public List transaction(List theResources) { + BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext); + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + Bundle resp = invokeClient(myContext, new BundleResponseHandler(null), invocation, myLogRequestAndResponse); + + return new ArrayList(resp.toListOfResources()); + } + + @Override + public IPatch patch() { + return new PatchInternal(); + } + + @Override + public IUpdate update() { + return new UpdateInternal(); + } + + @Override + public MethodOutcome update(IdDt theIdDt, IBaseResource theResource) { + BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext); + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); + final String resourceName = def.getName(); + + OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); + MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); + return resp; + } + + @Override + public MethodOutcome update(String theId, IBaseResource theResource) { + return update(new IdDt(theId), theResource); + } + + @Override + public IValidate validate() { + return new ValidateInternal(); + } + + @Override + public MethodOutcome validate(IBaseResource theResource) { + BaseHttpClientInvocation invocation; + if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { + invocation = ValidateMethodBindingDstu1.createValidateInvocation(theResource, null, myContext); + } else { + invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, theResource); + } + + if (isKeepResponses()) { + myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); + } + + RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); + final String resourceName = def.getName(); + + OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); + MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); + return resp; + } + + @Override + public T vread(final Class theType, IdDt theId) { + if (theId.hasVersionIdPart() == false) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue())); + } + return doReadOrVRead(theType, theId, true, null, null, false, null, null, null); + } + + /* also deprecated in interface */ + @Deprecated + @Override + public T vread(final Class theType, IdDt theId, IdDt theVersionId) { + return vread(theType, theId.withVersion(theVersionId.getIdPart())); + } + + @Override + public T vread(Class theType, String theId, String theVersionId) { + IdDt resId = new IdDt(toResourceName(theType), theId, theVersionId); + return vread(theType, resId); + } + + private static void addParam(Map> params, String parameterName, String parameterValue) { + if (!params.containsKey(parameterName)) { + params.put(parameterName, new ArrayList()); + } + params.get(parameterName).add(parameterValue); + } + + private static void addPreferHeader(PreferReturnEnum thePrefer, BaseHttpClientInvocation theInvocation) { + if (thePrefer != null) { + theInvocation.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + thePrefer.getHeaderValue()); + } + } + + private static String validateAndEscapeConditionalUrl(String theSearchUrl) { + Validate.notBlank(theSearchUrl, "Conditional URL can not be blank/null"); + StringBuilder b = new StringBuilder(); + boolean haveHadQuestionMark = false; + for (int i = 0; i < theSearchUrl.length(); i++) { + char nextChar = theSearchUrl.charAt(i); + if (!haveHadQuestionMark) { + if (nextChar == '?') { + haveHadQuestionMark = true; + } else if (!Character.isLetter(nextChar)) { + throw new IllegalArgumentException("Conditional URL must be in the format \"[ResourceType]?[Params]\" and must not have a base URL - Found: " + theSearchUrl); + } + b.append(nextChar); + } else { + switch (nextChar) { + case '|': + case '?': + case '$': + case ':': + b.append(UrlUtil.escape(Character.toString(nextChar))); + break; + default: + b.append(nextChar); + break; + } + } + } + return b.toString(); + } + + private abstract class BaseClientExecutable, Y> implements IClientExecutable { + + protected EncodingEnum myParamEncoding; + + private List> myPreferResponseTypes; + + protected Boolean myPrettyPrint; + + private boolean myQueryLogRequestAndResponse; + + private HashSet mySubsetElements; + + protected SummaryEnum mySummaryMode; + + @SuppressWarnings("unchecked") + @Override + public T andLogRequestAndResponse(boolean theLogRequestAndResponse) { + myQueryLogRequestAndResponse = theLogRequestAndResponse; + return (T) this; + } + + @SuppressWarnings("unchecked") + @Override + public T elementsSubset(String... theElements) { + if (theElements != null && theElements.length > 0) { + mySubsetElements = new HashSet(Arrays.asList(theElements)); + } else { + mySubsetElements = null; + } + return (T) this; + } + + @SuppressWarnings("unchecked") + @Override + public T encodedJson() { + myParamEncoding = EncodingEnum.JSON; + return (T) this; + } + + @SuppressWarnings("unchecked") + @Override + public T encodedXml() { + myParamEncoding = EncodingEnum.XML; + return (T) this; + } + + protected EncodingEnum getParamEncoding() { + return myParamEncoding; + } + + public List> getPreferResponseTypes() { + return myPreferResponseTypes; + } + + public List> getPreferResponseTypes(Class theDefault) { + if (myPreferResponseTypes != null) { + return myPreferResponseTypes; + } else { + return toTypeList(theDefault); + } + } + + protected HashSet getSubsetElements() { + return mySubsetElements; + } + + protected Z invoke(Map> theParams, IClientResponseHandler theHandler, BaseHttpClientInvocation theInvocation) { + // if (myParamEncoding != null) { + // theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType())); + // } + // + // if (myPrettyPrint != null) { + // theParams.put(Constants.PARAM_PRETTY, Collections.singletonList(myPrettyPrint.toString())); + // } + + if (isKeepResponses()) { + myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint); + } + + Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse, mySummaryMode, mySubsetElements); + return resp; + } + + protected IBaseResource parseResourceBody(String theResourceBody) { + EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody); + if (encoding == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); + } + return encoding.newParser(myContext).parseResource(theResourceBody); + } + + @SuppressWarnings("unchecked") + @Override + public T preferResponseType(Class theClass) { + myPreferResponseTypes = null; + if (theClass != null) { + myPreferResponseTypes = new ArrayList>(); + myPreferResponseTypes.add(theClass); + } + return (T) this; + } + + @SuppressWarnings("unchecked") + @Override + public T preferResponseTypes(List> theClass) { + myPreferResponseTypes = theClass; + return (T) this; + } + + @SuppressWarnings("unchecked") + @Override + public T prettyPrint() { + myPrettyPrint = true; + return (T) this; + } + + @SuppressWarnings("unchecked") + @Override + public T summaryMode(SummaryEnum theSummary) { + mySummaryMode = theSummary; + return ((T) this); + } + + } + + private final class BundleResponseHandler implements IClientResponseHandler { + + private Class myType; + + public BundleResponseHandler(Class theType) { + myType = theType; + } + + @Override + public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { + EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); + if (respType == null) { + throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); + } + IParser parser = respType.newParser(myContext); + return parser.parseBundle(myType, theResponseReader); + } + } + + private class CreateInternal extends BaseClientExecutable implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped { + + private CriterionList myCriterionList; + private String myId; + private PreferReturnEnum myPrefer; + private IBaseResource myResource; + private String myResourceBody; + private String mySearchUrl; + + @Override + public ICreateWithQueryTyped and(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public ICreateWithQuery conditional() { + myCriterionList = new CriterionList(); + return this; + } + + @Override + public ICreateTyped conditionalByUrl(String theSearchUrl) { + mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); + return this; + } + + @Override + public MethodOutcome execute() { + if (myResource == null) { + myResource = parseResourceBody(myResourceBody); + } + myId = getPreferredId(myResource, myId); + + // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding + if (getParamEncoding() != null) { + myResourceBody = null; + } + + BaseHttpClientInvocation invocation; + if (mySearchUrl != null) { + invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, mySearchUrl); + } else if (myCriterionList != null) { + invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, myCriterionList.toParamList()); + } else { + invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext); + } + + addPreferHeader(myPrefer, invocation); + + RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource); + final String resourceName = def.getName(); + + OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer); + + Map> params = new HashMap>(); + return invoke(params, binding, invocation); + + } + + @Override + public ICreateTyped prefer(PreferReturnEnum theReturn) { + myPrefer = theReturn; + return this; + } + + @Override + public ICreateTyped resource(IBaseResource theResource) { + Validate.notNull(theResource, "Resource can not be null"); + myResource = theResource; + return this; + } + + @Override + public ICreateTyped resource(String theResourceBody) { + Validate.notBlank(theResourceBody, "Body can not be null or blank"); + myResourceBody = theResourceBody; + return this; + } + + @Override + public ICreateWithQueryTyped where(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public CreateInternal withId(IdDt theId) { + myId = theId.getIdPart(); + return this; + } + + @Override + public CreateInternal withId(String theId) { + myId = theId; + return this; + } + + } + + private class CriterionList extends ArrayList { + + private static final long serialVersionUID = 1L; + + public void populateParamList(Map> theParams) { + for (ICriterionInternal next : this) { + String parameterName = next.getParameterName(); + String parameterValue = next.getParameterValue(myContext); + if (isNotBlank(parameterValue)) { + addParam(theParams, parameterName, parameterValue); + } + } + } + + public Map> toParamList() { + LinkedHashMap> retVal = new LinkedHashMap>(); + populateParamList(retVal); + return retVal; + } + + } + + private class DeleteInternal extends BaseClientExecutable implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped { + + private CriterionList myCriterionList; + private IIdType myId; + private String myResourceType; + private String mySearchUrl; + + @Override + public IDeleteWithQueryTyped and(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public IBaseOperationOutcome execute() { + HttpDeleteClientInvocation invocation; + if (myId != null) { + invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myId); + } else if (myCriterionList != null) { + Map> params = myCriterionList.toParamList(); + invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myResourceType, params); + } else { + invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), mySearchUrl); + } + OperationOutcomeResponseHandler binding = new OperationOutcomeResponseHandler(); + Map> params = new HashMap>(); + return invoke(params, binding, invocation); + } + + @Override + public IDeleteTyped resource(IBaseResource theResource) { + Validate.notNull(theResource, "theResource can not be null"); + IIdType id = theResource.getIdElement(); + Validate.notNull(id, "theResource.getIdElement() can not be null"); + if (id.hasResourceType() == false || id.hasIdPart() == false) { + throw new IllegalArgumentException("theResource.getId() must contain a resource type and logical ID at a minimum (e.g. Patient/1234), found: " + id.getValue()); + } + myId = id; + return this; + } + + @Override + public IDeleteTyped resourceById(IIdType theId) { + Validate.notNull(theId, "theId can not be null"); + if (theId.hasResourceType() == false || theId.hasIdPart() == false) { + throw new IllegalArgumentException("theId must contain a resource type and logical ID at a minimum (e.g. Patient/1234)found: " + theId.getValue()); + } + myId = theId; + return this; + } + + @Override + public IDeleteTyped resourceById(String theResourceType, String theLogicalId) { + Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); + if (myContext.getResourceDefinition(theResourceType) == null) { + throw new IllegalArgumentException("Unknown resource type"); + } + Validate.notBlank(theLogicalId, "theLogicalId can not be blank/null"); + if (theLogicalId.contains("/")) { + throw new IllegalArgumentException("LogicalId can not contain '/' (should only be the logical ID portion, not a qualified ID)"); + } + myId = new IdDt(theResourceType, theLogicalId); + return this; + } + + @Override + public IDeleteWithQuery resourceConditionalByType(Class theResourceType) { + Validate.notNull(theResourceType, "theResourceType can not be null"); + myCriterionList = new CriterionList(); + myResourceType = myContext.getResourceDefinition(theResourceType).getName(); + return this; + } + + @Override + public IDeleteWithQuery resourceConditionalByType(String theResourceType) { + Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); + if (myContext.getResourceDefinition(theResourceType) == null) { + throw new IllegalArgumentException("Unknown resource type: " + theResourceType); + } + myResourceType = theResourceType; + myCriterionList = new CriterionList(); + return this; + } + + @Override + public IDeleteTyped resourceConditionalByUrl(String theSearchUrl) { + mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); + return this; + } + + @Override + public IDeleteWithQueryTyped where(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private class FetchConformanceInternal extends BaseClientExecutable implements IFetchConformanceUntyped, IFetchConformanceTyped { + + private RuntimeResourceDefinition myType; + + @Override + public Object execute() { + ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass()); + HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); + return super.invoke(null, binding, invocation); + } + + @Override + public IFetchConformanceTyped ofType(Class theResourceType) { + Validate.notNull(theResourceType, "theResourceType must not be null"); + myType = myContext.getResourceDefinition(theResourceType); + if (myType == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); + } + return this; + } + + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private class GetPageInternal extends BaseClientExecutable, Object> implements IGetPageTyped { + + private Class myBundleType; + private String myUrl; + + public GetPageInternal(String theUrl) { + myUrl = theUrl; + } + + public GetPageInternal(String theUrl, Class theBundleType) { + myUrl = theUrl; + myBundleType = theBundleType; + } + + @Override + public Object execute() { + IClientResponseHandler binding; + if (myBundleType == null) { + binding = new BundleResponseHandler(null); + } else { + binding = new ResourceResponseHandler(myBundleType, getPreferResponseTypes()); + } + HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myContext, myUrl); + + Map> params = null; + return invoke(params, binding, invocation); + } + + } + + private class GetTagsInternal extends BaseClientExecutable implements IGetTags { + + private String myId; + private String myResourceName; + private String myVersionId; + + @Override + public TagList execute() { + + Map> params = new LinkedHashMap>(); + Map> initial = createExtraParams(); + if (initial != null) { + params.putAll(initial); + } + + TagListResponseHandler binding = new TagListResponseHandler(); + List urlFragments = new ArrayList(); + if (isNotBlank(myResourceName)) { + urlFragments.add(myResourceName); + if (isNotBlank(myId)) { + urlFragments.add(myId); + if (isNotBlank(myVersionId)) { + urlFragments.add(Constants.PARAM_HISTORY); + urlFragments.add(myVersionId); + } + } + } + urlFragments.add(Constants.PARAM_TAGS); + + HttpGetClientInvocation invocation = new HttpGetClientInvocation(myContext, params, urlFragments); + + return invoke(params, binding, invocation); + + } + + @Override + public IGetTags forResource(Class theClass) { + setResourceClass(theClass); + return this; + } + + @Override + public IGetTags forResource(Class theClass, String theId) { + setResourceClass(theClass); + myId = theId; + return this; + } + + @Override + public IGetTags forResource(Class theClass, String theId, String theVersionId) { + setResourceClass(theClass); + myId = theId; + myVersionId = theVersionId; + return this; + } + + private void setResourceClass(Class theClass) { + if (theClass != null) { + myResourceName = myContext.getResourceDefinition(theClass).getName(); + } else { + myResourceName = null; + } + } + + } + + @SuppressWarnings("rawtypes") + private class HistoryInternal extends BaseClientExecutable implements IHistory, IHistoryUntyped, IHistoryTyped { + + private Integer myCount; + private IIdType myId; + private Class myReturnType; + private IPrimitiveType mySince; + private Class myType; + + @SuppressWarnings("unchecked") + @Override + public IHistoryTyped andReturnBundle(Class theType) { + myReturnType = theType; + return this; + } + + @SuppressWarnings("unchecked") + @Override + public IHistoryTyped andReturnDstu1Bundle() { + return this; + } + + @Override + public IHistoryTyped count(Integer theCount) { + myCount = theCount; + return this; + } + + @SuppressWarnings("unchecked") + @Override + public Object execute() { + String resourceName; + String id; + if (myType != null) { + resourceName = myContext.getResourceDefinition(myType).getName(); + id = null; + } else if (myId != null) { + resourceName = myId.getResourceType(); + id = myId.getIdPart(); + } else { + resourceName = null; + id = null; + } + + HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, mySince, myCount); + + IClientResponseHandler handler; + if (myReturnType != null) { + handler = new ResourceResponseHandler(myReturnType, getPreferResponseTypes(myType)); + } else { + handler = new BundleResponseHandler(null); + } + + return invoke(null, handler, invocation); + } + + @Override + public IHistoryUntyped onInstance(IIdType theId) { + if (theId.hasResourceType() == false) { + throw new IllegalArgumentException("Resource ID does not have a resource type: " + theId.getValue()); + } + myId = theId; + return this; + } + + @Override + public IHistoryUntyped onServer() { + return this; + } + + @Override + public IHistoryUntyped onType(Class theResourceType) { + myType = theResourceType; + return this; + } + + @Override + public IHistoryTyped since(Date theCutoff) { + if (theCutoff != null) { + mySince = new InstantDt(theCutoff); + } else { + mySince = null; + } + return this; + } + + @Override + public IHistoryTyped since(IPrimitiveType theCutoff) { + mySince = theCutoff; + return this; + } + + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private final class LoadPageInternal implements IGetPage, IGetPageUntyped { + + private static final String PREV = "prev"; + private static final String PREVIOUS = "previous"; + private String myPageUrl; + + @Override + public IGetPageTyped andReturnBundle(Class theBundleType) { + Validate.notNull(theBundleType, "theBundleType must not be null"); + return new GetPageInternal(myPageUrl, theBundleType); + } + + @Override + public IGetPageTyped andReturnDstu1Bundle() { + return new GetPageInternal(myPageUrl); + } + + @Override + public IGetPageUntyped byUrl(String thePageUrl) { + if (isBlank(thePageUrl)) { + throw new IllegalArgumentException("thePagingUrl must not be blank or null"); + } + myPageUrl = thePageUrl; + return this; + } + + @Override + public IGetPageTyped next(Bundle theBundle) { + return new GetPageInternal(theBundle.getLinkNext().getValue()); + } + + @Override + public IGetPageTyped next(T theBundle) { + return nextOrPrevious("next", theBundle); + } + + private IGetPageTyped nextOrPrevious(String theWantRel, T theBundle) { + RuntimeResourceDefinition def = myContext.getResourceDefinition(theBundle); + List links = def.getChildByName("link").getAccessor().getValues(theBundle); + if (links == null || links.isEmpty()) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); + } + for (IBase nextLink : links) { + BaseRuntimeElementCompositeDefinition linkDef = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(nextLink.getClass()); + List rel = linkDef.getChildByName("relation").getAccessor().getValues(nextLink); + if (rel == null || rel.isEmpty()) { + continue; + } + String relation = ((IPrimitiveType) rel.get(0)).getValueAsString(); + if (theWantRel.equals(relation) || (theWantRel == PREVIOUS && PREV.equals(relation))) { + List urls = linkDef.getChildByName("url").getAccessor().getValues(nextLink); + if (urls == null || urls.isEmpty()) { + continue; + } + String url = ((IPrimitiveType) urls.get(0)).getValueAsString(); + if (isBlank(url)) { + continue; + } + return (IGetPageTyped) byUrl(url).andReturnBundle(theBundle.getClass()); + } + } + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); + } + + @Override + public IGetPageTyped previous(Bundle theBundle) { + return new GetPageInternal(theBundle.getLinkPrevious().getValue()); + } + + @Override + public IGetPageTyped previous(T theBundle) { + return nextOrPrevious(PREVIOUS, theBundle); + } + + @Override + public IGetPageTyped url(String thePageUrl) { + return new GetPageInternal(thePageUrl); + } + + } + + @SuppressWarnings("rawtypes") + private class MetaInternal extends BaseClientExecutable implements IMeta, IMetaAddOrDeleteUnsourced, IMetaGetUnsourced, IMetaAddOrDeleteSourced { + + private IIdType myId; + private IBaseMetaType myMeta; + private Class myMetaType; + private String myOnType; + private MetaOperation myOperation; + + @Override + public IMetaAddOrDeleteUnsourced add() { + myOperation = MetaOperation.ADD; + return this; + } + + @Override + public IMetaAddOrDeleteUnsourced delete() { + myOperation = MetaOperation.DELETE; + return this; + } + + @SuppressWarnings("unchecked") + @Override + public Object execute() { + + BaseHttpClientInvocation invocation = null; + + IBaseParameters parameters = ParametersUtil.newInstance(myContext); + switch (myOperation) { + case ADD: + ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta"); + invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-add", parameters, false); + break; + case DELETE: + ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta"); + invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-delete", parameters, false); + break; + case GET: + if (myId != null) { + invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, myId.getIdPart(), "$meta", parameters, true); + } else if (myOnType != null) { + invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, null, "$meta", parameters, true); + } else { + invocation = OperationMethodBinding.createOperationInvocation(myContext, null, null, "$meta", parameters, true); + } + break; + } + + // Should not happen + if (invocation == null) { + throw new IllegalStateException(); + } + + IClientResponseHandler handler; + handler = new MetaParametersResponseHandler(myMetaType); + return invoke(null, handler, invocation); + } + + @Override + public IClientExecutable fromResource(IIdType theId) { + setIdInternal(theId); + return this; + } + + @Override + public IClientExecutable fromServer() { + return this; + } + + @Override + public IClientExecutable fromType(String theResourceName) { + Validate.notBlank(theResourceName, "theResourceName must not be blank"); + myOnType = theResourceName; + return this; + } + + @SuppressWarnings("unchecked") + @Override + public IMetaGetUnsourced get(Class theType) { + myMetaType = theType; + myOperation = MetaOperation.GET; + return this; + } + + @SuppressWarnings("unchecked") + @Override + public IClientExecutable, T> meta(T theMeta) { + Validate.notNull(theMeta, "theMeta must not be null"); + myMeta = theMeta; + myMetaType = myMeta.getClass(); + return this; + } + + @Override + public IMetaAddOrDeleteSourced onResource(IIdType theId) { + setIdInternal(theId); + return this; + } + + private void setIdInternal(IIdType theId) { + Validate.notBlank(theId.getResourceType(), "theId must contain a resource type"); + Validate.notBlank(theId.getIdPart(), "theId must contain an ID part"); + myOnType = theId.getResourceType(); + myId = theId; + } + + } + + private enum MetaOperation { + + ADD, DELETE, GET + } + + private final class MetaParametersResponseHandler implements IClientResponseHandler { + + private Class myType; + + public MetaParametersResponseHandler(Class theMetaType) { + myType = theMetaType; + } + + @SuppressWarnings("unchecked") + @Override + public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { + EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); + if (respType == null) { + throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); + } + IParser parser = respType.newParser(myContext); + RuntimeResourceDefinition type = myContext.getResourceDefinition("Parameters"); + IBaseResource retVal = parser.parseResource(type.getImplementingClass(), theResponseReader); + + BaseRuntimeChildDefinition paramChild = type.getChildByName("parameter"); + BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); + List parameter = paramChild.getAccessor().getValues(retVal); + if (parameter == null || parameter.isEmpty()) { + return (T) myContext.getElementDefinition(myType).newInstance(); + } + IBase param = parameter.get(0); + + List meta = paramChildElem.getChildByName("value[x]").getAccessor().getValues(param); + if (meta.isEmpty()) { + return (T) myContext.getElementDefinition(myType).newInstance(); + } + return (T) meta.get(0); + + } + } + + @SuppressWarnings("rawtypes") + private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput, IOperationUntypedWithInputAndPartialOutput, IOperationProcessMsg, IOperationProcessMsgMode { + + private IIdType myId; + private String myOperationName; + private IBaseParameters myParameters; + private RuntimeResourceDefinition myParametersDef; + private Class myType; + private boolean myUseHttpGet; + private IBaseBundle myMsgBundle; + private Class myReturnResourceType; + private String myResponseUrl; + private Boolean myIsAsync = null; + + @SuppressWarnings("unchecked") + private void addParam(String theName, IBase theValue) { + BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter"); + BaseRuntimeElementCompositeDefinition parameterElem = (BaseRuntimeElementCompositeDefinition) parameterChild.getChildByName("parameter"); + + IBase parameter = parameterElem.newInstance(); + parameterChild.getMutator().addValue(myParameters, parameter); + + IPrimitiveType name = (IPrimitiveType) myContext.getElementDefinition("string").newInstance(); + name.setValue(theName); + parameterElem.getChildByName("name").getMutator().setValue(parameter, name); + + if (theValue instanceof IBaseDatatype) { + BaseRuntimeElementDefinition datatypeDef = myContext.getElementDefinition(theValue.getClass()); + if (datatypeDef instanceof IRuntimeDatatypeDefinition) { + Class profileOf = ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf(); + if (profileOf != null) { + datatypeDef = myContext.getElementDefinition(profileOf); + } + } + String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName()); + BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName); + childByName.getMutator().setValue(parameter, theValue); + } else if (theValue instanceof IBaseResource) { + parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue); + } else { + throw new IllegalArgumentException("Don't know how to handle parameter of type " + theValue.getClass()); + } + } + + private void addParam(String theName, IQueryParameterType theValue) { + IPrimitiveType stringType = ParametersUtil.createString(myContext, theValue.getValueAsQueryToken(myContext)); + addParam(theName, stringType); + } + + @Override + public IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue) { + Validate.notEmpty(theName, "theName must not be null"); + Validate.notNull(theValue, "theValue must not be null"); + addParam(theName, theValue); + return this; + } + + @Override + public IOperationUntypedWithInputAndPartialOutput andSearchParameter(String theName, IQueryParameterType theValue) { + addParam(theName, theValue); + + return this; + } + + @SuppressWarnings("unchecked") + @Override + public Object execute() { + + if (myOperationName != null && myOperationName.equals(Constants.EXTOP_PROCESS_MESSAGE)) { + Map> urlParams = new LinkedHashMap>(); + //Set Url parameter Async and Response-Url + if (myIsAsync != null) { + urlParams.put(Constants.PARAM_ASYNC, Arrays.asList(String.valueOf(myIsAsync))); } - @Override - public IOperationUnnamed onInstance(IIdType theId) { - myId = theId; - return this; - } + if (myResponseUrl != null && isNotBlank(myResponseUrl)) { + urlParams.put(Constants.PARAM_RESPONSE_URL, Arrays.asList(String.valueOf(myResponseUrl))); + } + //If is $process-message operation + BaseHttpClientInvocation invocation = OperationMethodBinding.createProcessMsgInvocation(myContext, myOperationName, myMsgBundle, urlParams); + + ResourceResponseHandler handler = new ResourceResponseHandler(); + handler.setPreferResponseTypes(getPreferResponseTypes(myType)); + + Object retVal = invoke(null, handler, invocation); + return retVal; + } else { + String resourceName; + String id; + if (myType != null) { + resourceName = myContext.getResourceDefinition(myType).getName(); + id = null; + } else if (myId != null) { + resourceName = myId.getResourceType(); + id = myId.getIdPart(); + } else { + resourceName = null; + id = null; + } - @Override - public IOperationUnnamed onServer() { - return this; - } + BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet); - @Override - public IOperationUnnamed onType(Class theResourceType) { - myType = theResourceType; - return this; - } + ResourceResponseHandler handler = new ResourceResponseHandler(); + handler.setPreferResponseTypes(getPreferResponseTypes(myType)); - @Override - public IOperationUntypedWithInput useHttpGet() { - myUseHttpGet = true; - return this; - } + Object retVal = invoke(null, handler, invocation); + if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { + return retVal; + } else { + RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); + IBaseResource parameters = def.newInstance(); - @SuppressWarnings("unchecked") - @Override - public IOperationUntypedWithInput withNoParameters(Class theOutputParameterType) { - Validate.notNull(theOutputParameterType, "theOutputParameterType may not be null"); - RuntimeResourceDefinition def = myContext.getResourceDefinition(theOutputParameterType); - if (def == null) { - throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName()); - } - if (!"Parameters".equals(def.getName())) { - throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName() - + " is a resource named: " + def.getName()); - } - myParameters = (IBaseParameters) def.newInstance(); - return this; - } + BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); + BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); + IBase parameter = paramChildElem.newInstance(); + paramChild.getMutator().addValue(parameters, parameter); - @SuppressWarnings("unchecked") - @Override - public IOperationUntypedWithInputAndPartialOutput withParameter(Class theParameterType, String theName, IBase theValue) { - Validate.notNull(theParameterType, "theParameterType must not be null"); - Validate.notEmpty(theName, "theName must not be null"); - Validate.notNull(theValue, "theValue must not be null"); + BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); + resourceElem.getMutator().addValue(parameter, (IBase) retVal); - myParametersDef = myContext.getResourceDefinition(theParameterType); - myParameters = (IBaseParameters) myParametersDef.newInstance(); + return parameters; + } + } - addParam(theName, theValue); + } - return this; - } + @Override + public IOperationUntyped named(String theName) { + Validate.notBlank(theName, "theName can not be null"); + myOperationName = theName; + return this; + } - @SuppressWarnings({ "unchecked" }) - @Override - public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) { - Validate.notNull(theParameters, "theParameters can not be null"); - myParameters = theParameters; - return this; - } + @Override + public IOperationProcessMsg processMessage() { + myOperationName = Constants.EXTOP_PROCESS_MESSAGE; + return this; + } - @SuppressWarnings("unchecked") - @Override - public IOperationUntypedWithInputAndPartialOutput withSearchParameter(Class theParameterType, String theName, IQueryParameterType theValue) { - Validate.notNull(theParameterType, "theParameterType must not be null"); - Validate.notEmpty(theName, "theName must not be null"); - Validate.notNull(theValue, "theValue must not be null"); + @Override + public IOperationUnnamed onInstance(IIdType theId) { + myId = theId; + return this; + } - myParametersDef = myContext.getResourceDefinition(theParameterType); - myParameters = (IBaseParameters) myParametersDef.newInstance(); + @Override + public IOperationUnnamed onServer() { + return this; + } - addParam(theName, theValue); + @Override + public IOperationUnnamed onType(Class theResourceType) { + myType = theResourceType; + return this; + } - return this; - } + @Override + public IOperationUntypedWithInput useHttpGet() { + myUseHttpGet = true; + return this; + } + @SuppressWarnings("unchecked") + @Override + public IOperationUntypedWithInput withNoParameters(Class theOutputParameterType) { + Validate.notNull(theOutputParameterType, "theOutputParameterType may not be null"); + RuntimeResourceDefinition def = myContext.getResourceDefinition(theOutputParameterType); + if (def == null) { + throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName()); + } + if (!"Parameters".equals(def.getName())) { + throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName() + + " is a resource named: " + def.getName()); + } + myParameters = (IBaseParameters) def.newInstance(); + return this; + } - @Override - public IOperationProcessMsg setMessageBundle(IBaseBundle theMsgBundle) { + @SuppressWarnings("unchecked") + @Override + public IOperationUntypedWithInputAndPartialOutput withParameter(Class theParameterType, String theName, IBase theValue) { + Validate.notNull(theParameterType, "theParameterType must not be null"); + Validate.notEmpty(theName, "theName must not be null"); + Validate.notNull(theValue, "theValue must not be null"); + + myParametersDef = myContext.getResourceDefinition(theParameterType); + myParameters = (IBaseParameters) myParametersDef.newInstance(); + + addParam(theName, theValue); + + return this; + } + + @SuppressWarnings({"unchecked"}) + @Override + public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) { + Validate.notNull(theParameters, "theParameters can not be null"); + myParameters = theParameters; + return this; + } + + @SuppressWarnings("unchecked") + @Override + public IOperationUntypedWithInputAndPartialOutput withSearchParameter(Class theParameterType, String theName, IQueryParameterType theValue) { + Validate.notNull(theParameterType, "theParameterType must not be null"); + Validate.notEmpty(theName, "theName must not be null"); + Validate.notNull(theValue, "theValue must not be null"); + + myParametersDef = myContext.getResourceDefinition(theParameterType); + myParameters = (IBaseParameters) myParametersDef.newInstance(); + + addParam(theName, theValue); + + return this; + } + + @Override + public IOperationProcessMsgMode setMessageBundle(IBaseBundle theMsgBundle) { + + Validate.notNull(theMsgBundle, "theMsgBundle must not be null"); + /* Validate.isTrue(theMsgBundle.getType().getValueAsEnum() == BundleTypeEnum.MESSAGE); + Validate.isTrue(theMsgBundle.getEntries().size() > 0); + Validate.notNull(theMsgBundle.getEntries().get(0).getResource(), "Message Bundle first entry must be a MessageHeader resource"); + Validate.isTrue(theMsgBundle.getEntries().get(0).getResource().getResourceName().equals("MessageHeader"), "Message Bundle first entry must be a MessageHeader resource"); + */ + myMsgBundle = (IBaseBundle) theMsgBundle; + return this; + } + + @Override + public IOperationProcessMsg setResponseUrlParam(String responseUrl) { + Validate.notEmpty(responseUrl, "responseUrl must not be null"); + Validate.matchesPattern(responseUrl, "^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "responseUrl must be a valid URL"); + myResponseUrl = responseUrl; + return this; + } + + @Override + public IOperationUntypedWithInput returnResourceType(Class theReturnType) { + Validate.notNull(theReturnType, "theReturnType must not be null"); + Validate.isTrue(IBaseResource.class.isAssignableFrom(theReturnType), "theReturnType must be a class which extends from IBaseResource"); + myReturnResourceType = theReturnType; + return this; + } + + @Override + public IOperationProcessMsgMode asynchronous(Class theResponseClass) { + myIsAsync = true; + Validate.notNull(theResponseClass, "theReturnType must not be null"); + Validate.isTrue(IBaseResource.class.isAssignableFrom(theResponseClass), "theReturnType must be a class which extends from IBaseResource"); + myReturnResourceType = theResponseClass; + return this; + } + + @Override + public IOperationProcessMsgMode synchronous(Class theResponseClass) { + myIsAsync = false; + Validate.notNull(theResponseClass, "theReturnType must not be null"); + Validate.isTrue(IBaseResource.class.isAssignableFrom(theResponseClass), "theReturnType must be a class which extends from IBaseResource"); + myReturnResourceType = theResponseClass; + return this; + } + + + + - Validate.notNull(theMsgBundle, "theMsgBundle must not be null"); - /* Validate.isTrue(theMsgBundle.getType().getValueAsEnum() == BundleTypeEnum.MESSAGE); - Validate.isTrue(theMsgBundle.getEntries().size() > 0); - Validate.notNull(theMsgBundle.getEntries().get(0).getResource(), "Message Bundle first entry must be a MessageHeader resource"); - Validate.isTrue(theMsgBundle.getEntries().get(0).getResource().getResourceName().equals("MessageHeader"), "Message Bundle first entry must be a MessageHeader resource"); - */ - myMsgBundle = (IBaseBundle) theMsgBundle; - return this; } - @Override - public IOperationProcessMsg setAsyncProcessingMode() { - setMessageAsync(true); - return this; + private final class OperationOutcomeResponseHandler implements IClientResponseHandler { + + @Override + public IBaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) + throws BaseServerResponseException { + EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); + if (respType == null) { + return null; + } + IParser parser = respType.newParser(myContext); + IBaseOperationOutcome retVal; + try { + // TODO: handle if something else than OO comes back + retVal = (IBaseOperationOutcome) parser.parseResource(theResponseReader); + } catch (DataFormatException e) { + ourLog.warn("Failed to parse OperationOutcome response", e); + return null; + } + MethodUtil.parseClientRequestResourceHeaders(null, theHeaders, retVal); + + return retVal; + } } - @Override - public IOperationProcessMsg setResponseUrlParam(String responseUrl) { - Validate.notEmpty(responseUrl, "responseUrl must not be null"); - Validate.matchesPattern(responseUrl, "^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]", "responseUrl must be a valid URL"); - setMessageResponseUrl(responseUrl); - return this; + private final class OutcomeResponseHandler implements IClientResponseHandler { + + private PreferReturnEnum myPrefer; + private final String myResourceName; + + private OutcomeResponseHandler(String theResourceName) { + myResourceName = theResourceName; + } + + private OutcomeResponseHandler(String theResourceName, PreferReturnEnum thePrefer) { + this(theResourceName); + myPrefer = thePrefer; + } + + @Override + public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { + MethodOutcome response = MethodUtil.process2xxResponse(myContext, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders); + if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) { + response.setCreated(true); + } + + if (myPrefer == PreferReturnEnum.REPRESENTATION) { + if (response.getResource() == null) { + if (response.getId() != null && isNotBlank(response.getId().getValue()) && response.getId().hasBaseUrl()) { + ourLog.info("Server did not return resource for Prefer-representation, going to fetch: {}", response.getId().getValue()); + IBaseResource resource = read().resource(response.getId().getResourceType()).withUrl(response.getId()).execute(); + response.setResource(resource); + } + } + } + + return response; + } } - - - - @Override - public IOperationUntypedWithInput returnResourceType(Class theReturnType) { - Validate.notNull(theReturnType, "theReturnType must not be null"); - Validate.isTrue(IBaseResource.class.isAssignableFrom(theReturnType), "theReturnType must be a class which extends from IBaseResource"); - myReturnResourceType = theReturnType; - return this; - } - - - } - - private final class OperationOutcomeResponseHandler implements IClientResponseHandler { - - @Override - public IBaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) - throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - return null; - } - IParser parser = respType.newParser(myContext); - IBaseOperationOutcome retVal; - try { - // TODO: handle if something else than OO comes back - retVal = (IBaseOperationOutcome) parser.parseResource(theResponseReader); - } catch (DataFormatException e) { - ourLog.warn("Failed to parse OperationOutcome response", e); - return null; - } - MethodUtil.parseClientRequestResourceHeaders(null, theHeaders, retVal); - - return retVal; - } - } - - private final class OutcomeResponseHandler implements IClientResponseHandler { - private PreferReturnEnum myPrefer; - private final String myResourceName; - - private OutcomeResponseHandler(String theResourceName) { - myResourceName = theResourceName; - } - - private OutcomeResponseHandler(String theResourceName, PreferReturnEnum thePrefer) { - this(theResourceName); - myPrefer = thePrefer; - } - - @Override - public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - MethodOutcome response = MethodUtil.process2xxResponse(myContext, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders); - if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) { - response.setCreated(true); - } - - if (myPrefer == PreferReturnEnum.REPRESENTATION) { - if (response.getResource() == null) { - if (response.getId() != null && isNotBlank(response.getId().getValue()) && response.getId().hasBaseUrl()) { - ourLog.info("Server did not return resource for Prefer-representation, going to fetch: {}", response.getId().getValue()); - IBaseResource resource = read().resource(response.getId().getResourceType()).withUrl(response.getId()).execute(); - response.setResource(resource); - } - } - } - - return response; - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private class ReadInternal extends BaseClientExecutable implements IRead, IReadTyped, IReadExecutable { - private IIdType myId; - private String myIfVersionMatches; - private ICallable myNotModifiedHandler; - private RuntimeResourceDefinition myType; - - @Override - public Object execute() {// AAA - if (myId.hasVersionIdPart()) { - return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); - } else { - return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); - } - } - - @Override - public IReadIfNoneMatch ifVersionMatches(String theVersion) { - myIfVersionMatches = theVersion; - return new IReadIfNoneMatch() { - - @Override - public IReadExecutable returnNull() { - myNotModifiedHandler = new ICallable() { - @Override - public Object call() { - return null; - } - }; - return ReadInternal.this; - } - - @Override - public IReadExecutable returnResource(final IBaseResource theInstance) { - myNotModifiedHandler = new ICallable() { - @Override - public Object call() { - return theInstance; - } - }; - return ReadInternal.this; - } - - @Override - public IReadExecutable throwNotModifiedException() { - myNotModifiedHandler = null; - return ReadInternal.this; - } - }; - } - - private void processUrl() { - String resourceType = myId.getResourceType(); - if (isBlank(resourceType)) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, myId)); - } - myType = myContext.getResourceDefinition(resourceType); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, myId)); - } - } - - @Override - public IReadTyped resource(Class theResourceType) { - Validate.notNull(theResourceType, "theResourceType must not be null"); - myType = myContext.getResourceDefinition(theResourceType); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); - } - return this; - } - - @Override - public IReadTyped resource(String theResourceAsText) { - Validate.notBlank(theResourceAsText, "You must supply a value for theResourceAsText"); - myType = myContext.getResourceDefinition(theResourceAsText); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceAsText)); - } - return this; - } - - @Override - public IReadExecutable withId(IIdType theId) { - Validate.notNull(theId, "The ID can not be null"); - Validate.notBlank(theId.getIdPart(), "The ID can not be blank"); - myId = theId.toUnqualified(); - return this; - } - - @Override - public IReadExecutable withId(Long theId) { - Validate.notNull(theId, "The ID can not be null"); - myId = new IdDt(myType.getName(), theId); - return this; - } - - @Override - public IReadExecutable withId(String theId) { - Validate.notBlank(theId, "The ID can not be blank"); - if (theId.indexOf('/') == -1) { - myId = new IdDt(myType.getName(), theId); - } else { - myId = new IdDt(theId); - } - return this; - } - - @Override - public IReadExecutable withIdAndVersion(String theId, String theVersion) { - Validate.notBlank(theId, "The ID can not be blank"); - myId = new IdDt(myType.getName(), theId, theVersion); - return this; - } - - @Override - public IReadExecutable withUrl(IIdType theUrl) { - Validate.notNull(theUrl, "theUrl can not be null"); - myId = theUrl; - processUrl(); - return this; - } - - @Override - public IReadExecutable withUrl(String theUrl) { - myId = new IdDt(theUrl); - processUrl(); - return this; - } - - } - - private final class ResourceListResponseHandler implements IClientResponseHandler> { - - private Class myType; - - public ResourceListResponseHandler(Class theType) { - myType = theType; - } - - @SuppressWarnings("unchecked") - @Override - public List invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) - throws BaseServerResponseException { - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { - Class bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass(); - ResourceResponseHandler handler = new ResourceResponseHandler((Class) bundleType); - IBaseResource response = handler.invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders); - IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory(); - bundleFactory.initializeWithBundleResource(response); - return bundleFactory.toListOfResources(); - } else { - return new ArrayList(new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources()); - } - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private class SearchInternal extends BaseClientExecutable, Object> implements IQuery, IUntypedQuery { - - private String myCompartmentName; - private CriterionList myCriterion = new CriterionList(); - private List myInclude = new ArrayList(); - private DateRangeParam myLastUpdated; - private Integer myParamLimit; - private List> myProfiles = new ArrayList>(); - private String myResourceId; - private String myResourceName; - private Class myResourceType; - private Class myReturnBundleType; - private List myRevInclude = new ArrayList(); - private SearchStyleEnum mySearchStyle; - private String mySearchUrl; - private List mySecurity = new ArrayList(); - private List mySort = new ArrayList(); - private List myTags = new ArrayList(); - - public SearchInternal() { - myResourceType = null; - myResourceName = null; - mySearchUrl = null; - } - - @Override - public IQuery and(ICriterion theCriterion) { - myCriterion.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IQuery byUrl(String theSearchUrl) { - Validate.notBlank(theSearchUrl, "theSearchUrl must not be blank/null"); - - if (theSearchUrl.startsWith("http://") || theSearchUrl.startsWith("https://")) { - mySearchUrl = theSearchUrl; - int qIndex = mySearchUrl.indexOf('?'); - if (qIndex != -1) { - mySearchUrl = mySearchUrl.substring(0, qIndex) + validateAndEscapeConditionalUrl(mySearchUrl.substring(qIndex)); - } - } else { - String searchUrl = theSearchUrl; - if (searchUrl.startsWith("/")) { - searchUrl = searchUrl.substring(1); - } - if (!searchUrl.matches("[a-zA-Z]+($|\\?.*)")) { - 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]"); - } - int qIndex = searchUrl.indexOf('?'); - if (qIndex == -1) { - mySearchUrl = getUrlBase() + '/' + searchUrl; - } else { - mySearchUrl = getUrlBase() + '/' + validateAndEscapeConditionalUrl(searchUrl); - } - } - return this; - } - - @Override - public IQuery count(int theLimitTo) { - if (theLimitTo > 0) { - myParamLimit = theLimitTo; - } else { - myParamLimit = null; - } - return this; - } - - @Override - public IBase execute() { - - Map> params = new LinkedHashMap>(); - // Map> initial = createExtraParams(); - // if (initial != null) { - // params.putAll(initial); - // } - - myCriterion.populateParamList(params); - - for (TokenParam next : myTags) { - addParam(params, Constants.PARAM_TAG, next.getValueAsQueryToken(myContext)); - } - - for (TokenParam next : mySecurity) { - addParam(params, Constants.PARAM_SECURITY, next.getValueAsQueryToken(myContext)); - } - - for (Collection profileUris : myProfiles) { - StringBuilder builder = new StringBuilder(); - for (Iterator profileItr = profileUris.iterator(); profileItr.hasNext();) { - builder.append(profileItr.next()); - if (profileItr.hasNext()) { - builder.append(','); - } - } - addParam(params, Constants.PARAM_PROFILE, builder.toString()); - } - - for (Include next : myInclude) { - if (next.isRecurse()) { - addParam(params, Constants.PARAM_INCLUDE_RECURSE, next.getValue()); - } else { - addParam(params, Constants.PARAM_INCLUDE, next.getValue()); - } - } - - for (Include next : myRevInclude) { - addParam(params, Constants.PARAM_REVINCLUDE, next.getValue()); - } - - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) { - SortSpec rootSs = null; - SortSpec lastSs = null; - for (SortInternal next : mySort) { - SortSpec nextSortSpec = new SortSpec(); - nextSortSpec.setParamName(next.getParamValue()); - nextSortSpec.setOrder(next.getDirection()); - if (rootSs == null) { - rootSs = nextSortSpec; - } else { - lastSs.setChain(nextSortSpec); - } - lastSs = nextSortSpec; - } - if (rootSs != null) { - addParam(params, Constants.PARAM_SORT, SortParameter.createSortStringDstu3(rootSs)); - } - } else { - for (SortInternal next : mySort) { - addParam(params, next.getParamName(), next.getParamValue()); - } - } - - if (myParamLimit != null) { - addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit)); - } - - if (myLastUpdated != null) { - for (DateParam next : myLastUpdated.getValuesAsQueryTokens()) { - addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken(myContext)); - } - } - - if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) { - throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify " - + "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"); - } - - IClientResponseHandler binding; - if (myReturnBundleType != null) { - binding = new ResourceResponseHandler(myReturnBundleType, getPreferResponseTypes(myResourceType)); - } else { - binding = new BundleResponseHandler(myResourceType); - } - - IdDt resourceId = myResourceId != null ? new IdDt(myResourceId) : null; - - BaseHttpClientInvocation invocation; - if (mySearchUrl != null) { - invocation = SearchMethodBinding.createSearchInvocation(myContext, mySearchUrl, params); - } else { - invocation = SearchMethodBinding.createSearchInvocation(myContext, myResourceName, params, resourceId, myCompartmentName, mySearchStyle); - } - - return invoke(params, binding, invocation); - - } - - @Override - public IQuery forAllResources() { - return this; - } - - @Override - public IQuery forResource(Class theResourceType) { - setType(theResourceType); - return this; - } - - @Override - public IQuery forResource(String theResourceName) { - setType(theResourceName); - return this; - } - - @Override - public IQuery include(Include theInclude) { - myInclude.add(theInclude); - return this; - } - - @Override - public IQuery lastUpdated(DateRangeParam theLastUpdated) { - myLastUpdated = theLastUpdated; - return this; - } - - @Override - public IQuery limitTo(int theLimitTo) { - return count(theLimitTo); - } - - @Override - public IQuery returnBundle(Class theClass) { - if (theClass == null) { - throw new NullPointerException("theClass must not be null"); - } - myReturnBundleType = theClass; - return this; - } - - @Override - public IQuery revInclude(Include theInclude) { - myRevInclude.add(theInclude); - return this; - } - - private void setType(Class theResourceType) { - myResourceType = theResourceType; - RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType); - myResourceName = definition.getName(); - } - - private void setType(String theResourceName) { - myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass(); - myResourceName = theResourceName; - } - - @Override - public ISort sort() { - SortInternal retVal = new SortInternal(this); - mySort.add(retVal); - return retVal; - } - - @Override - public IQuery usingStyle(SearchStyleEnum theStyle) { - mySearchStyle = theStyle; - return this; - } - - @Override - public IQuery where(ICriterion theCriterion) { - myCriterion.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IQuery withIdAndCompartment(String theResourceId, String theCompartmentName) { - myResourceId = theResourceId; - myCompartmentName = theCompartmentName; - return this; - } - - @Override - public IQuery withProfile(String theProfileUri) { - Validate.notBlank(theProfileUri, "theProfileUri must not be null or empty"); - myProfiles.add(Collections.singletonList(theProfileUri)); - return this; - } - - @Override - public IQuery withAnyProfile(Collection theProfileUris) { - Validate.notEmpty(theProfileUris, "theProfileUris must not be null or empty"); - myProfiles.add(theProfileUris); - return this; - } - - @Override - public IQuery withSecurity(String theSystem, String theCode) { - Validate.notBlank(theCode, "theCode must not be null or empty"); - mySecurity.add(new TokenParam(theSystem, theCode)); - return this; - } - - @Override - public IQuery withTag(String theSystem, String theCode) { - Validate.notBlank(theCode, "theCode must not be null or empty"); - myTags.add(new TokenParam(theSystem, theCode)); - return this; - } - - } - - @SuppressWarnings("rawtypes") - private static class SortInternal implements ISort { - - private SearchInternal myFor; - private String myParamName; - private String myParamValue; - private SortOrderEnum myDirection; - - public SortInternal(SearchInternal theFor) { - myFor = theFor; - } - - @Override - public IQuery ascending(IParam theParam) { - myParamName = Constants.PARAM_SORT_ASC; - myDirection = SortOrderEnum.ASC; - myParamValue = theParam.getParamName(); - return myFor; - } - - @Override - public IQuery ascending(String theParam) { - myParamName = Constants.PARAM_SORT_ASC; - myDirection = SortOrderEnum.ASC; - myParamValue = theParam; - return myFor; - } - - @Override - public IQuery defaultOrder(IParam theParam) { - myParamName = Constants.PARAM_SORT; - myDirection = null; - myParamValue = theParam.getParamName(); - return myFor; - } - - @Override - public IQuery descending(IParam theParam) { - myParamName = Constants.PARAM_SORT_DESC; - myDirection = SortOrderEnum.DESC; - myParamValue = theParam.getParamName(); - return myFor; - } - - @Override - public IQuery descending(String theParam) { - myParamName = Constants.PARAM_SORT_DESC; - myDirection = SortOrderEnum.DESC; - myParamValue = theParam; - return myFor; - } - - public SortOrderEnum getDirection() { - return myDirection; - } - - public String getParamName() { - return myParamName; - } - - public String getParamValue() { - return myParamValue; - } - - } - - private final class StringResponseHandler implements IClientResponseHandler { - - @Override - public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) - throws IOException, BaseServerResponseException { - return IOUtils.toString(theResponseReader); - } - } - - private final class TagListResponseHandler implements IClientResponseHandler { - - @Override - public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); - } - IParser parser = respType.newParser(myContext); - return parser.parseTagList(theResponseReader); - } - } - - private final class TransactionExecutable extends BaseClientExecutable, T> implements ITransactionTyped { - - private IBaseBundle myBaseBundle; - private Bundle myBundle; - private String myRawBundle; - private EncodingEnum myRawBundleEncoding; - private List myResources; - - public TransactionExecutable(Bundle theResources) { - myBundle = theResources; - } - - public TransactionExecutable(IBaseBundle theBundle) { - myBaseBundle = theBundle; - } - - public TransactionExecutable(List theResources) { - myResources = theResources; - } - - public TransactionExecutable(String theBundle) { - myRawBundle = theBundle; - myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle); - if (myRawBundleEncoding == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public T execute() { - Map> params = new HashMap>(); - if (myResources != null) { - ResourceListResponseHandler binding = new ResourceListResponseHandler(null); - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myResources, myContext); - return (T) invoke(params, binding, invocation); - } else if (myBaseBundle != null) { - ResourceResponseHandler binding = new ResourceResponseHandler(myBaseBundle.getClass(), getPreferResponseTypes()); - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBaseBundle, myContext); - return (T) invoke(params, binding, invocation); - } else if (myRawBundle != null) { - StringResponseHandler binding = new StringResponseHandler(); - /* - * If the user has explicitly requested a given encoding, we may need to re-encode the raw string - */ - if (getParamEncoding() != null) { - if (MethodUtil.detectEncodingNoDefault(myRawBundle) != getParamEncoding()) { - IBaseResource parsed = parseResourceBody(myRawBundle); - myRawBundle = getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed); - } - } - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myRawBundle, myContext); - return (T) invoke(params, binding, invocation); - } else { - BundleResponseHandler binding = new BundleResponseHandler(null); - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBundle, myContext); - return (T) invoke(params, binding, invocation); - } - } - - } - - private final class TransactionInternal implements ITransaction { - - @Override - public ITransactionTyped withBundle(Bundle theBundle) { - Validate.notNull(theBundle, "theBundle must not be null"); - return new TransactionExecutable(theBundle); - } - - @Override - public ITransactionTyped withBundle(String theBundle) { - Validate.notBlank(theBundle, "theBundle must not be null"); - return new TransactionExecutable(theBundle); - } - - @Override - public ITransactionTyped withBundle(T theBundle) { - Validate.notNull(theBundle, "theBundle must not be null"); - return new TransactionExecutable(theBundle); - } - - @Override - public ITransactionTyped> withResources(List theResources) { - Validate.notNull(theResources, "theResources must not be null"); - return new TransactionExecutable>(theResources); - } - - } - - private class PatchInternal extends BaseClientExecutable implements IPatch, IPatchWithBody, IPatchExecutable, IPatchWithQuery, IPatchWithQueryTyped { - - private CriterionList myCriterionList; - private IIdType myId; - private PreferReturnEnum myPrefer; - private PatchTypeEnum myPatchType; - private String myPatchBody; - private String mySearchUrl; - private String myResourceType; - - @Override - public IPatchWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IPatchWithQuery conditional(String theResourceType) { - Validate.notBlank(theResourceType, "theResourceType must not be null"); - myResourceType = theResourceType; - myCriterionList = new CriterionList(); - return this; - } - - // TODO: This is not longer used.. Deprecate it or just remove it? - @Override - public IPatchWithBody conditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public MethodOutcome execute() { - - if (myPatchType == null) { - throw new InvalidRequestException("No patch type supplied, cannot invoke server"); - } - if (myPatchBody == null) { - throw new InvalidRequestException("No patch body supplied, cannot invoke server"); - } - - BaseHttpClientInvocation invocation; - if (isNotBlank(mySearchUrl)) { - invocation = MethodUtil.createPatchInvocation(myContext, mySearchUrl, myPatchType, myPatchBody); - } else if (myCriterionList != null) { - invocation = MethodUtil.createPatchInvocation(myContext, myPatchType, myPatchBody, myResourceType, myCriterionList.toParamList()); - } else { - if (myId == null || myId.hasIdPart() == false) { - throw new InvalidRequestException("No ID supplied for resource to patch, can not invoke server"); - } - invocation = MethodUtil.createPatchInvocation(myContext, myId, myPatchType, myPatchBody); - } - - addPreferHeader(myPrefer, invocation); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(null, myPrefer); - - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - - } - - @Override - public IPatchExecutable prefer(PreferReturnEnum theReturn) { - myPrefer = theReturn; - return this; - } - - - @Override - public IPatchWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IPatchExecutable withId(IIdType theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (theId.hasIdPart() == false) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IPatchExecutable withId(String theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (isBlank(theId)) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId); - } - myId = new IdDt(theId); - return this; - } - - @Override - public IPatchWithBody withBody(String thePatchBody) { - Validate.notBlank(thePatchBody, "thePatchBody must not be blank"); - - myPatchBody = thePatchBody; - - EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(thePatchBody); - if (encoding == EncodingEnum.XML) { - myPatchType = PatchTypeEnum.XML_PATCH; - } else if (encoding == EncodingEnum.JSON) { - myPatchType = PatchTypeEnum.JSON_PATCH; - } else { - throw new IllegalArgumentException("Unable to determine encoding of patch"); - } - - return this; - } - - @Override - public IPatchWithQuery conditional(Class theClass) { - Validate.notNull(theClass, "theClass must not be null"); - String resourceType = myContext.getResourceDefinition(theClass).getName(); - return conditional(resourceType); - } - - } - - private class UpdateInternal extends BaseClientExecutable implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped { - - private CriterionList myCriterionList; - private IIdType myId; - private PreferReturnEnum myPrefer; - private IBaseResource myResource; - private String myResourceBody; - private String mySearchUrl; - - @Override - public IUpdateWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IUpdateWithQuery conditional() { - myCriterionList = new CriterionList(); - return this; - } - - @Override - public IUpdateTyped conditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public MethodOutcome execute() { - if (myResource == null) { - myResource = parseResourceBody(myResourceBody); - } - - // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding - if (getParamEncoding() != null) { - myResourceBody = null; - } - - BaseHttpClientInvocation invocation; - if (mySearchUrl != null) { - invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, mySearchUrl); - } else if (myCriterionList != null) { - invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, myCriterionList.toParamList()); - } else { - if (myId == null) { - myId = myResource.getIdElement(); - } - - if (myId == null || myId.hasIdPart() == false) { - throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server"); - } - invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext); - } - - addPreferHeader(myPrefer, invocation); - - RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer); - - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - - } - - @Override - public IUpdateExecutable prefer(PreferReturnEnum theReturn) { - myPrefer = theReturn; - return this; - } - - @Override - public IUpdateTyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "Resource can not be null"); - myResource = theResource; - return this; - } - - @Override - public IUpdateTyped resource(String theResourceBody) { - Validate.notBlank(theResourceBody, "Body can not be null or blank"); - myResourceBody = theResourceBody; - return this; - } - - @Override - public IUpdateWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IUpdateExecutable withId(IIdType theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (theId.hasIdPart() == false) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IUpdateExecutable withId(String theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (isBlank(theId)) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId); - } - myId = new IdDt(theId); - return this; - } - - } - - private class ValidateInternal extends BaseClientExecutable implements IValidate, IValidateUntyped { - private IBaseResource myResource; - - @Override - public MethodOutcome execute() { - BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, myResource); - ResourceResponseHandler handler = new ResourceResponseHandler(null, null); - IBaseOperationOutcome outcome = invoke(null, handler, invocation); - MethodOutcome retVal = new MethodOutcome(); - retVal.setOperationOutcome(outcome); - return retVal; - } - - @Override - public IValidateUntyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "theResource must not be null"); - myResource = theResource; - return this; - } - - @Override - public IValidateUntyped resource(String theResourceRaw) { - Validate.notBlank(theResourceRaw, "theResourceRaw must not be null or blank"); - myResource = parseResourceBody(theResourceRaw); - - EncodingEnum enc = MethodUtil.detectEncodingNoDefault(theResourceRaw); - if (enc == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); - } - switch (enc) { - case XML: - encodedXml(); - break; - case JSON: - encodedJson(); - break; - } - return this; - } - - } + @SuppressWarnings({"rawtypes", "unchecked"}) + private class ReadInternal extends BaseClientExecutable implements IRead, IReadTyped, IReadExecutable { + + private IIdType myId; + private String myIfVersionMatches; + private ICallable myNotModifiedHandler; + private RuntimeResourceDefinition myType; + + @Override + public Object execute() {// AAA + if (myId.hasVersionIdPart()) { + return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); + } else { + return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); + } + } + + @Override + public IReadIfNoneMatch ifVersionMatches(String theVersion) { + myIfVersionMatches = theVersion; + return new IReadIfNoneMatch() { + + @Override + public IReadExecutable returnNull() { + myNotModifiedHandler = new ICallable() { + @Override + public Object call() { + return null; + } + }; + return ReadInternal.this; + } + + @Override + public IReadExecutable returnResource(final IBaseResource theInstance) { + myNotModifiedHandler = new ICallable() { + @Override + public Object call() { + return theInstance; + } + }; + return ReadInternal.this; + } + + @Override + public IReadExecutable throwNotModifiedException() { + myNotModifiedHandler = null; + return ReadInternal.this; + } + }; + } + + private void processUrl() { + String resourceType = myId.getResourceType(); + if (isBlank(resourceType)) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, myId)); + } + myType = myContext.getResourceDefinition(resourceType); + if (myType == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, myId)); + } + } + + @Override + public IReadTyped resource(Class theResourceType) { + Validate.notNull(theResourceType, "theResourceType must not be null"); + myType = myContext.getResourceDefinition(theResourceType); + if (myType == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); + } + return this; + } + + @Override + public IReadTyped resource(String theResourceAsText) { + Validate.notBlank(theResourceAsText, "You must supply a value for theResourceAsText"); + myType = myContext.getResourceDefinition(theResourceAsText); + if (myType == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceAsText)); + } + return this; + } + + @Override + public IReadExecutable withId(IIdType theId) { + Validate.notNull(theId, "The ID can not be null"); + Validate.notBlank(theId.getIdPart(), "The ID can not be blank"); + myId = theId.toUnqualified(); + return this; + } + + @Override + public IReadExecutable withId(Long theId) { + Validate.notNull(theId, "The ID can not be null"); + myId = new IdDt(myType.getName(), theId); + return this; + } + + @Override + public IReadExecutable withId(String theId) { + Validate.notBlank(theId, "The ID can not be blank"); + if (theId.indexOf('/') == -1) { + myId = new IdDt(myType.getName(), theId); + } else { + myId = new IdDt(theId); + } + return this; + } + + @Override + public IReadExecutable withIdAndVersion(String theId, String theVersion) { + Validate.notBlank(theId, "The ID can not be blank"); + myId = new IdDt(myType.getName(), theId, theVersion); + return this; + } + + @Override + public IReadExecutable withUrl(IIdType theUrl) { + Validate.notNull(theUrl, "theUrl can not be null"); + myId = theUrl; + processUrl(); + return this; + } + + @Override + public IReadExecutable withUrl(String theUrl) { + myId = new IdDt(theUrl); + processUrl(); + return this; + } + + } + + private final class ResourceListResponseHandler implements IClientResponseHandler> { + + private Class myType; + + public ResourceListResponseHandler(Class theType) { + myType = theType; + } + + @SuppressWarnings("unchecked") + @Override + public List invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) + throws BaseServerResponseException { + if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { + Class bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass(); + ResourceResponseHandler handler = new ResourceResponseHandler((Class) bundleType); + IBaseResource response = handler.invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders); + IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory(); + bundleFactory.initializeWithBundleResource(response); + return bundleFactory.toListOfResources(); + } else { + return new ArrayList(new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources()); + } + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private class SearchInternal extends BaseClientExecutable, Object> implements IQuery, IUntypedQuery { + + private String myCompartmentName; + private CriterionList myCriterion = new CriterionList(); + private List myInclude = new ArrayList(); + private DateRangeParam myLastUpdated; + private Integer myParamLimit; + private List> myProfiles = new ArrayList>(); + private String myResourceId; + private String myResourceName; + private Class myResourceType; + private Class myReturnBundleType; + private List myRevInclude = new ArrayList(); + private SearchStyleEnum mySearchStyle; + private String mySearchUrl; + private List mySecurity = new ArrayList(); + private List mySort = new ArrayList(); + private List myTags = new ArrayList(); + + public SearchInternal() { + myResourceType = null; + myResourceName = null; + mySearchUrl = null; + } + + @Override + public IQuery and(ICriterion theCriterion) { + myCriterion.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public IQuery byUrl(String theSearchUrl) { + Validate.notBlank(theSearchUrl, "theSearchUrl must not be blank/null"); + + if (theSearchUrl.startsWith("http://") || theSearchUrl.startsWith("https://")) { + mySearchUrl = theSearchUrl; + int qIndex = mySearchUrl.indexOf('?'); + if (qIndex != -1) { + mySearchUrl = mySearchUrl.substring(0, qIndex) + validateAndEscapeConditionalUrl(mySearchUrl.substring(qIndex)); + } + } else { + String searchUrl = theSearchUrl; + if (searchUrl.startsWith("/")) { + searchUrl = searchUrl.substring(1); + } + if (!searchUrl.matches("[a-zA-Z]+($|\\?.*)")) { + 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]"); + } + int qIndex = searchUrl.indexOf('?'); + if (qIndex == -1) { + mySearchUrl = getUrlBase() + '/' + searchUrl; + } else { + mySearchUrl = getUrlBase() + '/' + validateAndEscapeConditionalUrl(searchUrl); + } + } + return this; + } + + @Override + public IQuery count(int theLimitTo) { + if (theLimitTo > 0) { + myParamLimit = theLimitTo; + } else { + myParamLimit = null; + } + return this; + } + + @Override + public IBase execute() { + + Map> params = new LinkedHashMap>(); + // Map> initial = createExtraParams(); + // if (initial != null) { + // params.putAll(initial); + // } + + myCriterion.populateParamList(params); + + for (TokenParam next : myTags) { + addParam(params, Constants.PARAM_TAG, next.getValueAsQueryToken(myContext)); + } + + for (TokenParam next : mySecurity) { + addParam(params, Constants.PARAM_SECURITY, next.getValueAsQueryToken(myContext)); + } + + for (Collection profileUris : myProfiles) { + StringBuilder builder = new StringBuilder(); + for (Iterator profileItr = profileUris.iterator(); profileItr.hasNext();) { + builder.append(profileItr.next()); + if (profileItr.hasNext()) { + builder.append(','); + } + } + addParam(params, Constants.PARAM_PROFILE, builder.toString()); + } + + for (Include next : myInclude) { + if (next.isRecurse()) { + addParam(params, Constants.PARAM_INCLUDE_RECURSE, next.getValue()); + } else { + addParam(params, Constants.PARAM_INCLUDE, next.getValue()); + } + } + + for (Include next : myRevInclude) { + addParam(params, Constants.PARAM_REVINCLUDE, next.getValue()); + } + + if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) { + SortSpec rootSs = null; + SortSpec lastSs = null; + for (SortInternal next : mySort) { + SortSpec nextSortSpec = new SortSpec(); + nextSortSpec.setParamName(next.getParamValue()); + nextSortSpec.setOrder(next.getDirection()); + if (rootSs == null) { + rootSs = nextSortSpec; + } else { + lastSs.setChain(nextSortSpec); + } + lastSs = nextSortSpec; + } + if (rootSs != null) { + addParam(params, Constants.PARAM_SORT, SortParameter.createSortStringDstu3(rootSs)); + } + } else { + for (SortInternal next : mySort) { + addParam(params, next.getParamName(), next.getParamValue()); + } + } + + if (myParamLimit != null) { + addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit)); + } + + if (myLastUpdated != null) { + for (DateParam next : myLastUpdated.getValuesAsQueryTokens()) { + addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken(myContext)); + } + } + + if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) { + throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify " + + "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"); + } + + IClientResponseHandler binding; + if (myReturnBundleType != null) { + binding = new ResourceResponseHandler(myReturnBundleType, getPreferResponseTypes(myResourceType)); + } else { + binding = new BundleResponseHandler(myResourceType); + } + + IdDt resourceId = myResourceId != null ? new IdDt(myResourceId) : null; + + BaseHttpClientInvocation invocation; + if (mySearchUrl != null) { + invocation = SearchMethodBinding.createSearchInvocation(myContext, mySearchUrl, params); + } else { + invocation = SearchMethodBinding.createSearchInvocation(myContext, myResourceName, params, resourceId, myCompartmentName, mySearchStyle); + } + + return invoke(params, binding, invocation); + + } + + @Override + public IQuery forAllResources() { + return this; + } + + @Override + public IQuery forResource(Class theResourceType) { + setType(theResourceType); + return this; + } + + @Override + public IQuery forResource(String theResourceName) { + setType(theResourceName); + return this; + } + + @Override + public IQuery include(Include theInclude) { + myInclude.add(theInclude); + return this; + } + + @Override + public IQuery lastUpdated(DateRangeParam theLastUpdated) { + myLastUpdated = theLastUpdated; + return this; + } + + @Override + public IQuery limitTo(int theLimitTo) { + return count(theLimitTo); + } + + @Override + public IQuery returnBundle(Class theClass) { + if (theClass == null) { + throw new NullPointerException("theClass must not be null"); + } + myReturnBundleType = theClass; + return this; + } + + @Override + public IQuery revInclude(Include theInclude) { + myRevInclude.add(theInclude); + return this; + } + + private void setType(Class theResourceType) { + myResourceType = theResourceType; + RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType); + myResourceName = definition.getName(); + } + + private void setType(String theResourceName) { + myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass(); + myResourceName = theResourceName; + } + + @Override + public ISort sort() { + SortInternal retVal = new SortInternal(this); + mySort.add(retVal); + return retVal; + } + + @Override + public IQuery usingStyle(SearchStyleEnum theStyle) { + mySearchStyle = theStyle; + return this; + } + + @Override + public IQuery where(ICriterion theCriterion) { + myCriterion.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public IQuery withIdAndCompartment(String theResourceId, String theCompartmentName) { + myResourceId = theResourceId; + myCompartmentName = theCompartmentName; + return this; + } + + @Override + public IQuery withProfile(String theProfileUri) { + Validate.notBlank(theProfileUri, "theProfileUri must not be null or empty"); + myProfiles.add(Collections.singletonList(theProfileUri)); + return this; + } + + @Override + public IQuery withAnyProfile(Collection theProfileUris) { + Validate.notEmpty(theProfileUris, "theProfileUris must not be null or empty"); + myProfiles.add(theProfileUris); + return this; + } + + @Override + public IQuery withSecurity(String theSystem, String theCode) { + Validate.notBlank(theCode, "theCode must not be null or empty"); + mySecurity.add(new TokenParam(theSystem, theCode)); + return this; + } + + @Override + public IQuery withTag(String theSystem, String theCode) { + Validate.notBlank(theCode, "theCode must not be null or empty"); + myTags.add(new TokenParam(theSystem, theCode)); + return this; + } + + } + + @SuppressWarnings("rawtypes") + private static class SortInternal implements ISort { + + private SearchInternal myFor; + private String myParamName; + private String myParamValue; + private SortOrderEnum myDirection; + + public SortInternal(SearchInternal theFor) { + myFor = theFor; + } + + @Override + public IQuery ascending(IParam theParam) { + myParamName = Constants.PARAM_SORT_ASC; + myDirection = SortOrderEnum.ASC; + myParamValue = theParam.getParamName(); + return myFor; + } + + @Override + public IQuery ascending(String theParam) { + myParamName = Constants.PARAM_SORT_ASC; + myDirection = SortOrderEnum.ASC; + myParamValue = theParam; + return myFor; + } + + @Override + public IQuery defaultOrder(IParam theParam) { + myParamName = Constants.PARAM_SORT; + myDirection = null; + myParamValue = theParam.getParamName(); + return myFor; + } + + @Override + public IQuery descending(IParam theParam) { + myParamName = Constants.PARAM_SORT_DESC; + myDirection = SortOrderEnum.DESC; + myParamValue = theParam.getParamName(); + return myFor; + } + + @Override + public IQuery descending(String theParam) { + myParamName = Constants.PARAM_SORT_DESC; + myDirection = SortOrderEnum.DESC; + myParamValue = theParam; + return myFor; + } + + public SortOrderEnum getDirection() { + return myDirection; + } + + public String getParamName() { + return myParamName; + } + + public String getParamValue() { + return myParamValue; + } + + } + + private final class StringResponseHandler implements IClientResponseHandler { + + @Override + public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) + throws IOException, BaseServerResponseException { + return IOUtils.toString(theResponseReader); + } + } + + private final class TagListResponseHandler implements IClientResponseHandler { + + @Override + public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { + EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); + if (respType == null) { + throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); + } + IParser parser = respType.newParser(myContext); + return parser.parseTagList(theResponseReader); + } + } + + private final class TransactionExecutable extends BaseClientExecutable, T> implements ITransactionTyped { + + private IBaseBundle myBaseBundle; + private Bundle myBundle; + private String myRawBundle; + private EncodingEnum myRawBundleEncoding; + private List myResources; + + public TransactionExecutable(Bundle theResources) { + myBundle = theResources; + } + + public TransactionExecutable(IBaseBundle theBundle) { + myBaseBundle = theBundle; + } + + public TransactionExecutable(List theResources) { + myResources = theResources; + } + + public TransactionExecutable(String theBundle) { + myRawBundle = theBundle; + myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle); + if (myRawBundleEncoding == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public T execute() { + Map> params = new HashMap>(); + if (myResources != null) { + ResourceListResponseHandler binding = new ResourceListResponseHandler(null); + BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myResources, myContext); + return (T) invoke(params, binding, invocation); + } else if (myBaseBundle != null) { + ResourceResponseHandler binding = new ResourceResponseHandler(myBaseBundle.getClass(), getPreferResponseTypes()); + BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBaseBundle, myContext); + return (T) invoke(params, binding, invocation); + } else if (myRawBundle != null) { + StringResponseHandler binding = new StringResponseHandler(); + /* + * If the user has explicitly requested a given encoding, we may need to re-encode the raw string + */ + if (getParamEncoding() != null) { + if (MethodUtil.detectEncodingNoDefault(myRawBundle) != getParamEncoding()) { + IBaseResource parsed = parseResourceBody(myRawBundle); + myRawBundle = getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed); + } + } + BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myRawBundle, myContext); + return (T) invoke(params, binding, invocation); + } else { + BundleResponseHandler binding = new BundleResponseHandler(null); + BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBundle, myContext); + return (T) invoke(params, binding, invocation); + } + } + + } + + private final class TransactionInternal implements ITransaction { + + @Override + public ITransactionTyped withBundle(Bundle theBundle) { + Validate.notNull(theBundle, "theBundle must not be null"); + return new TransactionExecutable(theBundle); + } + + @Override + public ITransactionTyped withBundle(String theBundle) { + Validate.notBlank(theBundle, "theBundle must not be null"); + return new TransactionExecutable(theBundle); + } + + @Override + public ITransactionTyped withBundle(T theBundle) { + Validate.notNull(theBundle, "theBundle must not be null"); + return new TransactionExecutable(theBundle); + } + + @Override + public ITransactionTyped> withResources(List theResources) { + Validate.notNull(theResources, "theResources must not be null"); + return new TransactionExecutable>(theResources); + } + + } + + private class PatchInternal extends BaseClientExecutable implements IPatch, IPatchWithBody, IPatchExecutable, IPatchWithQuery, IPatchWithQueryTyped { + + private CriterionList myCriterionList; + private IIdType myId; + private PreferReturnEnum myPrefer; + private PatchTypeEnum myPatchType; + private String myPatchBody; + private String mySearchUrl; + private String myResourceType; + + @Override + public IPatchWithQueryTyped and(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public IPatchWithQuery conditional(String theResourceType) { + Validate.notBlank(theResourceType, "theResourceType must not be null"); + myResourceType = theResourceType; + myCriterionList = new CriterionList(); + return this; + } + + // TODO: This is not longer used.. Deprecate it or just remove it? + @Override + public IPatchWithBody conditionalByUrl(String theSearchUrl) { + mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); + return this; + } + + @Override + public MethodOutcome execute() { + + if (myPatchType == null) { + throw new InvalidRequestException("No patch type supplied, cannot invoke server"); + } + if (myPatchBody == null) { + throw new InvalidRequestException("No patch body supplied, cannot invoke server"); + } + + BaseHttpClientInvocation invocation; + if (isNotBlank(mySearchUrl)) { + invocation = MethodUtil.createPatchInvocation(myContext, mySearchUrl, myPatchType, myPatchBody); + } else if (myCriterionList != null) { + invocation = MethodUtil.createPatchInvocation(myContext, myPatchType, myPatchBody, myResourceType, myCriterionList.toParamList()); + } else { + if (myId == null || myId.hasIdPart() == false) { + throw new InvalidRequestException("No ID supplied for resource to patch, can not invoke server"); + } + invocation = MethodUtil.createPatchInvocation(myContext, myId, myPatchType, myPatchBody); + } + + addPreferHeader(myPrefer, invocation); + + OutcomeResponseHandler binding = new OutcomeResponseHandler(null, myPrefer); + + Map> params = new HashMap>(); + return invoke(params, binding, invocation); + + } + + @Override + public IPatchExecutable prefer(PreferReturnEnum theReturn) { + myPrefer = theReturn; + return this; + } + + @Override + public IPatchWithQueryTyped where(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public IPatchExecutable withId(IIdType theId) { + if (theId == null) { + throw new NullPointerException("theId can not be null"); + } + if (theId.hasIdPart() == false) { + throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue()); + } + myId = theId; + return this; + } + + @Override + public IPatchExecutable withId(String theId) { + if (theId == null) { + throw new NullPointerException("theId can not be null"); + } + if (isBlank(theId)) { + throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId); + } + myId = new IdDt(theId); + return this; + } + + @Override + public IPatchWithBody withBody(String thePatchBody) { + Validate.notBlank(thePatchBody, "thePatchBody must not be blank"); + + myPatchBody = thePatchBody; + + EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(thePatchBody); + if (encoding == EncodingEnum.XML) { + myPatchType = PatchTypeEnum.XML_PATCH; + } else if (encoding == EncodingEnum.JSON) { + myPatchType = PatchTypeEnum.JSON_PATCH; + } else { + throw new IllegalArgumentException("Unable to determine encoding of patch"); + } + + return this; + } + + @Override + public IPatchWithQuery conditional(Class theClass) { + Validate.notNull(theClass, "theClass must not be null"); + String resourceType = myContext.getResourceDefinition(theClass).getName(); + return conditional(resourceType); + } + + } + + private class UpdateInternal extends BaseClientExecutable implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped { + + private CriterionList myCriterionList; + private IIdType myId; + private PreferReturnEnum myPrefer; + private IBaseResource myResource; + private String myResourceBody; + private String mySearchUrl; + + @Override + public IUpdateWithQueryTyped and(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public IUpdateWithQuery conditional() { + myCriterionList = new CriterionList(); + return this; + } + + @Override + public IUpdateTyped conditionalByUrl(String theSearchUrl) { + mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); + return this; + } + + @Override + public MethodOutcome execute() { + if (myResource == null) { + myResource = parseResourceBody(myResourceBody); + } + + // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding + if (getParamEncoding() != null) { + myResourceBody = null; + } + + BaseHttpClientInvocation invocation; + if (mySearchUrl != null) { + invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, mySearchUrl); + } else if (myCriterionList != null) { + invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, myCriterionList.toParamList()); + } else { + if (myId == null) { + myId = myResource.getIdElement(); + } + + if (myId == null || myId.hasIdPart() == false) { + throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server"); + } + invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext); + } + + addPreferHeader(myPrefer, invocation); + + RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource); + final String resourceName = def.getName(); + + OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer); + + Map> params = new HashMap>(); + return invoke(params, binding, invocation); + + } + + @Override + public IUpdateExecutable prefer(PreferReturnEnum theReturn) { + myPrefer = theReturn; + return this; + } + + @Override + public IUpdateTyped resource(IBaseResource theResource) { + Validate.notNull(theResource, "Resource can not be null"); + myResource = theResource; + return this; + } + + @Override + public IUpdateTyped resource(String theResourceBody) { + Validate.notBlank(theResourceBody, "Body can not be null or blank"); + myResourceBody = theResourceBody; + return this; + } + + @Override + public IUpdateWithQueryTyped where(ICriterion theCriterion) { + myCriterionList.add((ICriterionInternal) theCriterion); + return this; + } + + @Override + public IUpdateExecutable withId(IIdType theId) { + if (theId == null) { + throw new NullPointerException("theId can not be null"); + } + if (theId.hasIdPart() == false) { + throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue()); + } + myId = theId; + return this; + } + + @Override + public IUpdateExecutable withId(String theId) { + if (theId == null) { + throw new NullPointerException("theId can not be null"); + } + if (isBlank(theId)) { + throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId); + } + myId = new IdDt(theId); + return this; + } + + } + + private class ValidateInternal extends BaseClientExecutable implements IValidate, IValidateUntyped { + + private IBaseResource myResource; + + @Override + public MethodOutcome execute() { + BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, myResource); + ResourceResponseHandler handler = new ResourceResponseHandler(null, null); + IBaseOperationOutcome outcome = invoke(null, handler, invocation); + MethodOutcome retVal = new MethodOutcome(); + retVal.setOperationOutcome(outcome); + return retVal; + } + + @Override + public IValidateUntyped resource(IBaseResource theResource) { + Validate.notNull(theResource, "theResource must not be null"); + myResource = theResource; + return this; + } + + @Override + public IValidateUntyped resource(String theResourceRaw) { + Validate.notBlank(theResourceRaw, "theResourceRaw must not be null or blank"); + myResource = parseResourceBody(theResourceRaw); + + EncodingEnum enc = MethodUtil.detectEncodingNoDefault(theResourceRaw); + if (enc == null) { + throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); + } + switch (enc) { + case XML: + encodedXml(); + break; + case JSON: + encodedJson(); + break; + } + return this; + } + + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java index 97d3a230c87..8140f0fc936 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java @@ -89,22 +89,6 @@ public interface IRestfulClient { */ void setSummary(SummaryEnum theSummary); - /** - * Specifies that the server should process the message Synchronously or Asynchronously. This is only applicable to the - * $process-message server operation - * - * @param isAsync The async print flag to use in the request (default is false) - */ - void setMessageAsync(Boolean isAsync); - - /** - * Specifies that the server should send Asynchronous responses to this url. This is only applicable to the - * $process-message server operation - * - * @param isAsync The async print flag to use in the request (default is false) - */ - void setMessageResponseUrl(String responseUrl); - /** * Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)} */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperation.java index 63c4b62b392..10a476d719f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperation.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperation.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.rest.gclient; +import org.hl7.fhir.instance.model.api.IBaseResource; + /* * #%L * HAPI FHIR - Core Library @@ -19,9 +21,42 @@ package ca.uhn.fhir.rest.gclient; * limitations under the License. * #L% */ - public interface IOperation extends IBaseOn { - - + /** + * This operation is called $process-message as defined by FHIR + * DSTU2.

+ * Usage :
+ * + *
+     * Bundle response = client
+     * .operation()
+     * .onServer()
+     * .processMessage()
+     * .setResponseUrlParam("http://myserver/fhir")
+     * .setMessageBundle(msgBundle)
+     * .synchronous(Bundle.class)
+     * .execute();
+     * 
+     * //if you want to send an async message
+     * 
+     * OperationOutcome response = client
+     * .operation()
+     * .onServer()
+     * .processMessage()
+     * .setResponseUrlParam("http://myserver/fhir")
+     * .setMessageBundle(msgBundle)
+     * .asynchronous(OperationOutcome.class)
+     * .execute();
+     *
+     * 
+ *
+ * + * @see 2.4 Messaging + * using FHIR Resources + * + * @return An interface that defines the operation related to sending + * Messages to a Messaging Server + */ + IOperationProcessMsg processMessage(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java index 61251b45c98..05bf45d7d08 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsg.java @@ -6,19 +6,29 @@ package ca.uhn.fhir.rest.gclient; import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; /** * * @author HGS */ -public interface IOperationProcessMsg extends IClientExecutable, T> { +public interface IOperationProcessMsg{ - IOperationProcessMsg setMessageBundle(IBaseBundle theMsgBundle); - - IOperationProcessMsg setAsyncProcessingMode(); - - IOperationProcessMsg setResponseUrlParam(String respondToUri); + /** + * Set the Message Bundle to POST to the messaging server.
+ * After this call you must choose either the method synchronous or asynchronous to set the processing mode. + * + * @param + * @param theMsgBundle A Bundle of type message + * @return + */ + IOperationProcessMsgMode setMessageBundle(IBaseBundle theMsgBundle); + /** + * An optional query parameter indicating that responses from the receiving server should be sent to this url + * + * @param respondToUri The receiving endpoint to witch server response messages should be sent. + * @return + */ + IOperationProcessMsg setResponseUrlParam(String respondToUri); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsgMode.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsgMode.java new file mode 100644 index 00000000000..f598220121d --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationProcessMsgMode.java @@ -0,0 +1,34 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package ca.uhn.fhir.rest.gclient; + +import org.hl7.fhir.instance.model.api.IBaseResource; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2017 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +public interface IOperationProcessMsgMode extends IClientExecutable, T> { + + IOperationProcessMsgMode asynchronous(Class theResponseClass); + + IOperationProcessMsgMode synchronous(Class theResponseClass); +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java index b2815a06c8e..f138db21cc4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUnnamed.java @@ -24,6 +24,4 @@ package ca.uhn.fhir.rest.gclient; public interface IOperationUnnamed { IOperationUntyped named(String theName); - IOperationProcessMsg processMessage(); - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java index 1f3b5fb72a1..ca60729e630 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java @@ -63,370 +63,373 @@ import org.hl7.fhir.instance.model.api.IBaseBundle; public class OperationMethodBinding extends BaseResourceReturningMethodBinding { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationMethodBinding.class); - private BundleTypeEnum myBundleType; - private boolean myCanOperateAtInstanceLevel; - private boolean myCanOperateAtServerLevel; - private boolean myCanOperateAtTypeLevel; - private String myDescription; - private final boolean myIdempotent; - private final Integer myIdParamIndex; - private final String myName; - private final RestOperationTypeEnum myOtherOperatiopnType; - private List myReturnParams; - private final ReturnTypeEnum myReturnType; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationMethodBinding.class); + private BundleTypeEnum myBundleType; + private boolean myCanOperateAtInstanceLevel; + private boolean myCanOperateAtServerLevel; + private boolean myCanOperateAtTypeLevel; + private String myDescription; + private final boolean myIdempotent; + private final Integer myIdParamIndex; + private final String myName; + private final RestOperationTypeEnum myOtherOperatiopnType; + private List myReturnParams; + private final ReturnTypeEnum myReturnType; - protected OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, Class theOperationType, - OperationParam[] theReturnParams, BundleTypeEnum theBundleType) { - super(theReturnResourceType, theMethod, theContext, theProvider); + protected OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, Class theOperationType, + OperationParam[] theReturnParams, BundleTypeEnum theBundleType) { + super(theReturnResourceType, theMethod, theContext, theProvider); - myBundleType = theBundleType; - myIdempotent = theIdempotent; - myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); - if (myIdParamIndex != null) { - for (Annotation next : theMethod.getParameterAnnotations()[myIdParamIndex]) { - if (next instanceof IdParam) { - myCanOperateAtTypeLevel = ((IdParam) next).optional() == true; - } - } - } else { - myCanOperateAtTypeLevel = true; - } + myBundleType = theBundleType; + myIdempotent = theIdempotent; + myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext()); + if (myIdParamIndex != null) { + for (Annotation next : theMethod.getParameterAnnotations()[myIdParamIndex]) { + if (next instanceof IdParam) { + myCanOperateAtTypeLevel = ((IdParam) next).optional() == true; + } + } + } else { + myCanOperateAtTypeLevel = true; + } - Description description = theMethod.getAnnotation(Description.class); - if (description != null) { - myDescription = description.formalDefinition(); - if (isBlank(myDescription)) { - myDescription = description.shortDefinition(); - } - } - if (isBlank(myDescription)) { - myDescription = null; - } + Description description = theMethod.getAnnotation(Description.class); + if (description != null) { + myDescription = description.formalDefinition(); + if (isBlank(myDescription)) { + myDescription = description.shortDefinition(); + } + } + if (isBlank(myDescription)) { + myDescription = null; + } - if (isBlank(theOperationName)) { - throw new ConfigurationException("Method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getName() + " is annotated with @" + Operation.class.getSimpleName() + " but this annotation has no name defined"); - } - if (theOperationName.startsWith("$") == false) { - theOperationName = "$" + theOperationName; - } - myName = theOperationName; + if (isBlank(theOperationName)) { + throw new ConfigurationException("Method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getName() + " is annotated with @" + Operation.class.getSimpleName() + " but this annotation has no name defined"); + } + if (theOperationName.startsWith("$") == false) { + theOperationName = "$" + theOperationName; + } + myName = theOperationName; - if (theContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.DSTU1)) { - throw new ConfigurationException("@" + Operation.class.getSimpleName() + " methods are not supported on servers for FHIR version " + theContext.getVersion().getVersion().name() + " - Found one on class " + theMethod.getDeclaringClass().getName()); - } + if (theContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.DSTU1)) { + throw new ConfigurationException("@" + Operation.class.getSimpleName() + " methods are not supported on servers for FHIR version " + theContext.getVersion().getVersion().name() + " - Found one on class " + theMethod.getDeclaringClass().getName()); + } - if (theReturnTypeFromRp != null) { - setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName()); - } else { - if (Modifier.isAbstract(theOperationType.getModifiers()) == false) { - setResourceName(theContext.getResourceDefinition(theOperationType).getName()); - } else { - setResourceName(null); - } - } + if (theReturnTypeFromRp != null) { + setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName()); + } else { + if (Modifier.isAbstract(theOperationType.getModifiers()) == false) { + setResourceName(theContext.getResourceDefinition(theOperationType).getName()); + } else { + setResourceName(null); + } + } - if (theMethod.getReturnType().isAssignableFrom(Bundle.class)) { - throw new ConfigurationException("Can not return a DSTU1 bundle from an @" + Operation.class.getSimpleName() + " method. Found in method " + theMethod.getName() + " defined in type " + theMethod.getDeclaringClass().getName()); - } + if (theMethod.getReturnType().isAssignableFrom(Bundle.class)) { + throw new ConfigurationException("Can not return a DSTU1 bundle from an @" + Operation.class.getSimpleName() + " method. Found in method " + theMethod.getName() + " defined in type " + theMethod.getDeclaringClass().getName()); + } - if (theMethod.getReturnType().equals(IBundleProvider.class)) { - myReturnType = ReturnTypeEnum.BUNDLE; - } else { - myReturnType = ReturnTypeEnum.RESOURCE; - } + if (theMethod.getReturnType().equals(IBundleProvider.class)) { + myReturnType = ReturnTypeEnum.BUNDLE; + } else { + myReturnType = ReturnTypeEnum.RESOURCE; + } - if (getResourceName() == null) { - myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER; - } else if (myIdParamIndex == null) { - myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE; - } else { - myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE; - } + if (getResourceName() == null) { + myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER; + } else if (myIdParamIndex == null) { + myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE; + } else { + myOtherOperatiopnType = RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE; + } - myReturnParams = new ArrayList(); - if (theReturnParams != null) { - for (OperationParam next : theReturnParams) { - ReturnType type = new ReturnType(); - type.setName(next.name()); - type.setMin(next.min()); - type.setMax(next.max()); - if (type.getMax() == OperationParam.MAX_DEFAULT) { - type.setMax(1); - } - if (!next.type().equals(IBase.class)) { - if (next.type().isInterface() || Modifier.isAbstract(next.type().getModifiers())) { - throw new ConfigurationException("Invalid value for @OperationParam.type(): " + next.type().getName()); - } - type.setType(theContext.getElementDefinition(next.type()).getName()); - } - myReturnParams.add(type); - } - } + myReturnParams = new ArrayList(); + if (theReturnParams != null) { + for (OperationParam next : theReturnParams) { + ReturnType type = new ReturnType(); + type.setName(next.name()); + type.setMin(next.min()); + type.setMax(next.max()); + if (type.getMax() == OperationParam.MAX_DEFAULT) { + type.setMax(1); + } + if (!next.type().equals(IBase.class)) { + if (next.type().isInterface() || Modifier.isAbstract(next.type().getModifiers())) { + throw new ConfigurationException("Invalid value for @OperationParam.type(): " + next.type().getName()); + } + type.setType(theContext.getElementDefinition(next.type()).getName()); + } + myReturnParams.add(type); + } + } - if (myIdParamIndex != null) { - myCanOperateAtInstanceLevel = true; - } - if (getResourceName() == null) { - myCanOperateAtServerLevel = true; - } + if (myIdParamIndex != null) { + myCanOperateAtInstanceLevel = true; + } + if (getResourceName() == null) { + myCanOperateAtServerLevel = true; + } - } + } - public OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Operation theAnnotation) { - this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type(), theAnnotation.returnParameters(), theAnnotation.bundleType()); - } + public OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Operation theAnnotation) { + this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type(), theAnnotation.returnParameters(), theAnnotation.bundleType()); + } - public String getDescription() { - return myDescription; - } + public String getDescription() { + return myDescription; + } - /** - * Returns the name of the operation, starting with "$" - */ - public String getName() { - return myName; - } + /** + * Returns the name of the operation, starting with "$" + */ + public String getName() { + return myName; + } - @Override - protected BundleTypeEnum getResponseBundleType() { - return myBundleType; - } + @Override + protected BundleTypeEnum getResponseBundleType() { + return myBundleType; + } - @Override - public RestOperationTypeEnum getRestOperationType() { - return myOtherOperatiopnType; - } + @Override + public RestOperationTypeEnum getRestOperationType() { + return myOtherOperatiopnType; + } - public List getReturnParams() { - return Collections.unmodifiableList(myReturnParams); - } + public List getReturnParams() { + return Collections.unmodifiableList(myReturnParams); + } - @Override - public ReturnTypeEnum getReturnType() { - return myReturnType; - } + @Override + public ReturnTypeEnum getReturnType() { + return myReturnType; + } - @Override - public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) { - if (getResourceName() == null) { - if (isNotBlank(theRequest.getResourceName())) { - return false; - } - } else if (!getResourceName().equals(theRequest.getResourceName())) { - return false; - } + @Override + public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) { + if (getResourceName() == null) { + if (isNotBlank(theRequest.getResourceName())) { + return false; + } + } else if (!getResourceName().equals(theRequest.getResourceName())) { + return false; + } - if (!myName.equals(theRequest.getOperation())) { - return false; - } + if (!myName.equals(theRequest.getOperation())) { + return false; + } - RequestTypeEnum requestType = theRequest.getRequestType(); - if (requestType != RequestTypeEnum.GET && requestType != RequestTypeEnum.POST) { - // Operations can only be invoked with GET and POST - return false; - } + RequestTypeEnum requestType = theRequest.getRequestType(); + if (requestType != RequestTypeEnum.GET && requestType != RequestTypeEnum.POST) { + // Operations can only be invoked with GET and POST + return false; + } - boolean requestHasId = theRequest.getId() != null; - if (requestHasId) { - if (isCanOperateAtInstanceLevel() == false) { - return false; - } - } else { - if (myCanOperateAtTypeLevel == false) { - return false; - } - } + boolean requestHasId = theRequest.getId() != null; + if (requestHasId) { + if (isCanOperateAtInstanceLevel() == false) { + return false; + } + } else { + if (myCanOperateAtTypeLevel == false) { + return false; + } + } - return true; - } + return true; + } - @Override - public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException { - String id = null; - if (myIdParamIndex != null) { - IIdType idDt = (IIdType) theArgs[myIdParamIndex]; - id = idDt.getValue(); - } - IBaseParameters parameters = (IBaseParameters) getContext().getResourceDefinition("Parameters").newInstance(); + @Override + public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException { + String id = null; + if (myIdParamIndex != null) { + IIdType idDt = (IIdType) theArgs[myIdParamIndex]; + id = idDt.getValue(); + } + IBaseParameters parameters = (IBaseParameters) getContext().getResourceDefinition("Parameters").newInstance(); - if (theArgs != null) { - for (int idx = 0; idx < theArgs.length; idx++) { - IParameter nextParam = getParameters().get(idx); - nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], null, parameters); - } - } + if (theArgs != null) { + for (int idx = 0; idx < theArgs.length; idx++) { + IParameter nextParam = getParameters().get(idx); + nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], null, parameters); + } + } - return createOperationInvocation(getContext(), getResourceName(), id, myName, parameters, false); - } + return createOperationInvocation(getContext(), getResourceName(), id, myName, parameters, false); + } - @Override - public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException { - if (theRequest.getRequestType() == RequestTypeEnum.POST) { - IBaseResource requestContents = ResourceParameter.loadResourceFromRequest(theRequest, this, null); - theRequest.getUserData().put(OperationParameter.REQUEST_CONTENTS_USERDATA_KEY, requestContents); - } - return super.invokeServer(theServer, theRequest); - } + @Override + public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException { + if (theRequest.getRequestType() == RequestTypeEnum.POST) { + IBaseResource requestContents = ResourceParameter.loadResourceFromRequest(theRequest, this, null); + theRequest.getUserData().put(OperationParameter.REQUEST_CONTENTS_USERDATA_KEY, requestContents); + } + return super.invokeServer(theServer, theRequest); + } - @Override - public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException { - if (theRequest.getRequestType() == RequestTypeEnum.POST) { - // all good - } else if (theRequest.getRequestType() == RequestTypeEnum.GET) { - if (!myIdempotent) { - String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name()); - throw new MethodNotAllowedException(message, RequestTypeEnum.POST); - } - } else { - if (!myIdempotent) { - String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name()); - throw new MethodNotAllowedException(message, RequestTypeEnum.POST); - } else { - String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(), RequestTypeEnum.POST.name()); - throw new MethodNotAllowedException(message, RequestTypeEnum.GET, RequestTypeEnum.POST); - } - } + @Override + public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException { + if (theRequest.getRequestType() == RequestTypeEnum.POST) { + // all good + } else if (theRequest.getRequestType() == RequestTypeEnum.GET) { + if (!myIdempotent) { + String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name()); + throw new MethodNotAllowedException(message, RequestTypeEnum.POST); + } + } else { + if (!myIdempotent) { + String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.POST.name()); + throw new MethodNotAllowedException(message, RequestTypeEnum.POST); + } else { + String message = getContext().getLocalizer().getMessage(OperationMethodBinding.class, "methodNotSupported", theRequest.getRequestType(), RequestTypeEnum.GET.name(), RequestTypeEnum.POST.name()); + throw new MethodNotAllowedException(message, RequestTypeEnum.GET, RequestTypeEnum.POST); + } + } - if (myIdParamIndex != null) { - theMethodParams[myIdParamIndex] = theRequest.getId(); - } + if (myIdParamIndex != null) { + theMethodParams[myIdParamIndex] = theRequest.getId(); + } - Object response = invokeServerMethod(theServer, theRequest, theMethodParams); - IBundleProvider retVal = toResourceList(response); - return retVal; - } + Object response = invokeServerMethod(theServer, theRequest, theMethodParams); + IBundleProvider retVal = toResourceList(response); + return retVal; + } - public boolean isCanOperateAtInstanceLevel() { - return this.myCanOperateAtInstanceLevel; - } + public boolean isCanOperateAtInstanceLevel() { + return this.myCanOperateAtInstanceLevel; + } - public boolean isCanOperateAtServerLevel() { - return this.myCanOperateAtServerLevel; - } + public boolean isCanOperateAtServerLevel() { + return this.myCanOperateAtServerLevel; + } - public boolean isCanOperateAtTypeLevel() { - return myCanOperateAtTypeLevel; - } + public boolean isCanOperateAtTypeLevel() { + return myCanOperateAtTypeLevel; + } - public boolean isIdempotent() { - return myIdempotent; - } + public boolean isIdempotent() { + return myIdempotent; + } - @Override - protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) { - super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams); - theDetails.setResource((IBaseResource) theRequestDetails.getUserData().get(OperationParameter.REQUEST_CONTENTS_USERDATA_KEY)); - } + @Override + protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) { + super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams); + theDetails.setResource((IBaseResource) theRequestDetails.getUserData().get(OperationParameter.REQUEST_CONTENTS_USERDATA_KEY)); + } - public void setDescription(String theDescription) { - myDescription = theDescription; - } + public void setDescription(String theDescription) { + myDescription = theDescription; + } - public static BaseHttpClientInvocation createProcessMsgInvocation(FhirContext theContext, String theOperationName, IBaseBundle theInput) { - StringBuilder b = new StringBuilder(); - - if (b.length() > 0) { - b.append('/'); - } - if (!theOperationName.startsWith("$")) { - b.append("$"); - } - b.append(theOperationName); + public static BaseHttpClientInvocation createProcessMsgInvocation(FhirContext theContext, String theOperationName, IBaseBundle theInput, Map> urlParams) { + StringBuilder b = new StringBuilder(); - + if (b.length() > 0) { + b.append('/'); + } + if (!theOperationName.startsWith("$")) { + b.append("$"); + } + b.append(theOperationName); + + BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(urlParams,b, b.indexOf("?") == -1); + + return new HttpPostClientInvocation(theContext, theInput, b.toString()); + + } + + public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, boolean theUseHttpGet) { + StringBuilder b = new StringBuilder(); + if (theResourceName != null) { + b.append(theResourceName); + if (isNotBlank(theId)) { + b.append('/'); + b.append(theId); + } + } + if (b.length() > 0) { + b.append('/'); + } + if (!theOperationName.startsWith("$")) { + b.append("$"); + } + b.append(theOperationName); + + if (!theUseHttpGet) { return new HttpPostClientInvocation(theContext, theInput, b.toString()); - - } - public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, boolean theUseHttpGet) { - StringBuilder b = new StringBuilder(); - if (theResourceName != null) { - b.append(theResourceName); - if (isNotBlank(theId)) { - b.append('/'); - b.append(theId); - } - } - if (b.length() > 0) { - b.append('/'); - } - if (!theOperationName.startsWith("$")) { - b.append("$"); - } - b.append(theOperationName); + } else { + FhirTerser t = theContext.newTerser(); + List parameters = t.getValues(theInput, "Parameters.parameter"); - if (!theUseHttpGet) { - return new HttpPostClientInvocation(theContext, theInput, b.toString()); - } else { - FhirTerser t = theContext.newTerser(); - List parameters = t.getValues(theInput, "Parameters.parameter"); + Map> params = new LinkedHashMap>(); + for (Object nextParameter : parameters) { + IPrimitiveType nextNameDt = (IPrimitiveType) t.getSingleValueOrNull((IBase) nextParameter, "name"); + if (nextNameDt == null || nextNameDt.isEmpty()) { + ourLog.warn("Ignoring input parameter with no value in Parameters.parameter.name in operation client invocation"); + continue; + } + String nextName = nextNameDt.getValueAsString(); + if (!params.containsKey(nextName)) { + params.put(nextName, new ArrayList()); + } - Map> params = new LinkedHashMap>(); - for (Object nextParameter : parameters) { - IPrimitiveType nextNameDt = (IPrimitiveType) t.getSingleValueOrNull((IBase) nextParameter, "name"); - if (nextNameDt == null || nextNameDt.isEmpty()) { - ourLog.warn("Ignoring input parameter with no value in Parameters.parameter.name in operation client invocation"); - continue; - } - String nextName = nextNameDt.getValueAsString(); - if (!params.containsKey(nextName)) { - params.put(nextName, new ArrayList()); - } + IBaseDatatype value = (IBaseDatatype) t.getSingleValueOrNull((IBase) nextParameter, "value[x]"); + if (value == null) { + continue; + } + if (!(value instanceof IPrimitiveType)) { + throw new IllegalArgumentException("Can not invoke operation as HTTP GET when it has parameters with a composite (non priitive) datatype as the value. Found value: " + value.getClass().getName()); + } + IPrimitiveType primitive = (IPrimitiveType) value; + params.get(nextName).add(primitive.getValueAsString()); + } + return new HttpGetClientInvocation(theContext, params, b.toString()); + } + } - IBaseDatatype value = (IBaseDatatype) t.getSingleValueOrNull((IBase) nextParameter, "value[x]"); - if (value == null) { - continue; - } - if (!(value instanceof IPrimitiveType)) { - throw new IllegalArgumentException("Can not invoke operation as HTTP GET when it has parameters with a composite (non priitive) datatype as the value. Found value: " + value.getClass().getName()); - } - IPrimitiveType primitive = (IPrimitiveType) value; - params.get(nextName).add(primitive.getValueAsString()); - } - return new HttpGetClientInvocation(theContext, params, b.toString()); - } - } + public static class ReturnType { - public static class ReturnType { - private int myMax; - private int myMin; - private String myName; - /** - * http://hl7-fhir.github.io/valueset-operation-parameter-type.html - */ - private String myType; + private int myMax; + private int myMin; + private String myName; + /** + * http://hl7-fhir.github.io/valueset-operation-parameter-type.html + */ + private String myType; - public int getMax() { - return myMax; - } + public int getMax() { + return myMax; + } - public int getMin() { - return myMin; - } + public int getMin() { + return myMin; + } - public String getName() { - return myName; - } + public String getName() { + return myName; + } - public String getType() { - return myType; - } + public String getType() { + return myType; + } - public void setMax(int theMax) { - myMax = theMax; - } + public void setMax(int theMax) { + myMax = theMax; + } - public void setMin(int theMin) { - myMin = theMin; - } + public void setMin(int theMin) { + myMin = theMin; + } - public void setName(String theName) { - myName = theName; - } + public void setName(String theName) { + myName = theName; + } - public void setType(String theType) { - myType = theType; - } - } + public void setType(String theType) { + myType = theType; + } + } } diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 515ecd7f0c0..dd441fe2f9a 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -173,7 +173,7 @@ org.apache.maven.plugins maven-surefire-plugin - ${argLine} -Dfile.encoding=UTF-8 -Xmx712m + -Dfile.encoding=UTF-8 -Xmx712m diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index e9329c97c96..9c9c18bd926 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -2703,16 +2703,6 @@ public class GenericClientDstu2Test { public void unregisterInterceptor(IClientInterceptor theInterceptor) { //nothing } - - @Override - public void setMessageAsync(Boolean isAsync) { - - } - - @Override - public void setMessageResponseUrl(String responseUrl) { - - } } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java index 02423967593..677767ddc68 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/MessageClientDstu2Test.java @@ -1,21 +1,13 @@ package ca.uhn.fhir.rest.client; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import java.io.InputStream; import java.io.StringReader; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; - -import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicStatusLine; @@ -31,22 +23,15 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.MessageHeader; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; -import ca.uhn.fhir.model.dstu2.resource.Parameters; -import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.ResponseTypeEnum; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.util.TestUtil; import java.util.Date; import java.util.UUID; -import org.hl7.fhir.instance.model.api.IBaseBundle; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -98,7 +83,7 @@ public class MessageClientDstu2Test { } }); - IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.196:83/fhirServer"); + IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.93:83/fhirServer"); client.setEncoding(EncodingEnum.JSON); @@ -108,17 +93,16 @@ public class MessageClientDstu2Test { "MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/"); // Invoke $process-message - Object response = client + OperationOutcome response = client .operation() - .onServer() .processMessage() - .setAsyncProcessingMode() .setResponseUrlParam("http://myserver/fhir") .setMessageBundle(msgBundle) + .asynchronous(OperationOutcome.class) .execute(); //System.out.println(response); - assertEquals("http://192.168.4.196:83/fhirServer/$process-message?_format=json&async=true&response-url=http%3A%2F%2Fmyserver%2Ffhir", capt.getAllValues().get(0).getURI().toASCIIString()); + assertEquals("http://192.168.4.93:83/fhirServer/$process-message?async=true&response-url=http%3A%2F%2Fmyserver%2Ffhir&_format=json", capt.getAllValues().get(0).getURI().toASCIIString()); assertEquals("POST", capt.getAllValues().get(0).getRequestLine().getMethod()); //assertEquals("", extractBody(capt, 0)); //assertNotNull(response.getOperationOutcome()); @@ -145,7 +129,7 @@ public class MessageClientDstu2Test { } }); - IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.196:83/fhirServer"); + IGenericClient client = ourCtx.getRestfulClientFactory().newGenericClient("http://192.168.4.93:83/fhirServer"); client.setEncoding(EncodingEnum.JSON); @@ -155,16 +139,15 @@ public class MessageClientDstu2Test { "MySource", "http://myServer/fhir/", "MyDestination", "http://myDestinationServer/fhir/"); // Invoke $process-message - Object response = client - .operation() - .onServer() + Bundle response = client + .operation() .processMessage() - .setResponseUrlParam("http://myserver/fhir") .setMessageBundle(msgBundle) + .synchronous(Bundle.class) .execute(); //System.out.println(response); - assertEquals("http://192.168.4.196:83/fhirServer/$process-message?_format=json&response-url=http%3A%2F%2Fmyserver%2Ffhir", capt.getAllValues().get(0).getURI().toASCIIString()); + assertEquals("http://192.168.4.93:83/fhirServer/$process-message?async=false&_format=json", capt.getAllValues().get(0).getURI().toASCIIString()); assertEquals("POST", capt.getAllValues().get(0).getRequestLine().getMethod()); //assertEquals("", extractBody(capt, 0)); //assertNotNull(response.getOperationOutcome()); From da9daf86c03289018c1299b17d680aefcbacde29 Mon Sep 17 00:00:00 2001 From: Hugo Soares Date: Thu, 9 Feb 2017 15:19:08 +0000 Subject: [PATCH 3/3] Merge remote-tracking branch 'refs/remotes/jamesagnew/master' # Conflicts: # hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java --- .../uhn/fhir/rest/client/GenericClient.java | 2596 +---------------- 1 file changed, 37 insertions(+), 2559 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index a128c5d5734..f8fc16718ac 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -43,7 +43,6 @@ import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; -import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.*; @@ -51,69 +50,6 @@ import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; - -import ca.uhn.fhir.rest.gclient.IClientExecutable; -import ca.uhn.fhir.rest.gclient.ICreate; -import ca.uhn.fhir.rest.gclient.ICreateTyped; -import ca.uhn.fhir.rest.gclient.ICreateWithQuery; -import ca.uhn.fhir.rest.gclient.ICreateWithQueryTyped; -import ca.uhn.fhir.rest.gclient.ICriterion; -import ca.uhn.fhir.rest.gclient.ICriterionInternal; -import ca.uhn.fhir.rest.gclient.IDelete; -import ca.uhn.fhir.rest.gclient.IDeleteTyped; -import ca.uhn.fhir.rest.gclient.IDeleteWithQuery; -import ca.uhn.fhir.rest.gclient.IDeleteWithQueryTyped; -import ca.uhn.fhir.rest.gclient.IFetchConformanceTyped; -import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped; -import ca.uhn.fhir.rest.gclient.IGetPage; -import ca.uhn.fhir.rest.gclient.IGetPageTyped; -import ca.uhn.fhir.rest.gclient.IGetPageUntyped; -import ca.uhn.fhir.rest.gclient.IGetTags; -import ca.uhn.fhir.rest.gclient.IHistory; -import ca.uhn.fhir.rest.gclient.IHistoryTyped; -import ca.uhn.fhir.rest.gclient.IHistoryUntyped; -import ca.uhn.fhir.rest.gclient.IMeta; -import ca.uhn.fhir.rest.gclient.IMetaAddOrDeleteSourced; -import ca.uhn.fhir.rest.gclient.IMetaAddOrDeleteUnsourced; -import ca.uhn.fhir.rest.gclient.IMetaGetUnsourced; -import ca.uhn.fhir.rest.gclient.IOperation; -import ca.uhn.fhir.rest.gclient.IOperationProcessMsg; -import ca.uhn.fhir.rest.gclient.IOperationUnnamed; -import ca.uhn.fhir.rest.gclient.IOperationUntyped; -import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput; -import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; -import ca.uhn.fhir.rest.gclient.IParam; -import ca.uhn.fhir.rest.gclient.IQuery; -import ca.uhn.fhir.rest.gclient.IRead; -import ca.uhn.fhir.rest.gclient.IReadExecutable; -import ca.uhn.fhir.rest.gclient.IReadIfNoneMatch; -import ca.uhn.fhir.rest.gclient.IReadTyped; -import ca.uhn.fhir.rest.gclient.ISort; -import ca.uhn.fhir.rest.gclient.ITransaction; -import ca.uhn.fhir.rest.gclient.ITransactionTyped; -import ca.uhn.fhir.rest.gclient.IUntypedQuery; -import ca.uhn.fhir.rest.gclient.IUpdate; -import ca.uhn.fhir.rest.gclient.IUpdateExecutable; -import ca.uhn.fhir.rest.gclient.IUpdateTyped; -import ca.uhn.fhir.rest.gclient.IUpdateWithQuery; -import ca.uhn.fhir.rest.gclient.IUpdateWithQueryTyped; -import ca.uhn.fhir.rest.gclient.IValidate; -import ca.uhn.fhir.rest.gclient.IValidateUntyped; -import ca.uhn.fhir.rest.method.DeleteMethodBinding; -import ca.uhn.fhir.rest.method.HistoryMethodBinding; -import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation; -import ca.uhn.fhir.rest.method.HttpGetClientInvocation; -import ca.uhn.fhir.rest.method.HttpSimpleGetClientInvocation; -import ca.uhn.fhir.rest.method.IClientResponseHandler; -import ca.uhn.fhir.rest.method.MethodUtil; -import ca.uhn.fhir.rest.method.OperationMethodBinding; -import ca.uhn.fhir.rest.method.ReadMethodBinding; -import ca.uhn.fhir.rest.method.SearchMethodBinding; -import ca.uhn.fhir.rest.method.SearchStyleEnum; -import ca.uhn.fhir.rest.method.SortParameter; -import ca.uhn.fhir.rest.method.TransactionMethodBinding; -import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu1; -import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu2Plus; import ca.uhn.fhir.rest.gclient.*; import ca.uhn.fhir.rest.method.*; import ca.uhn.fhir.rest.param.DateParam; @@ -134,6 +70,7 @@ import ca.uhn.fhir.util.UrlUtil; * @author Doug Martin (Regenstrief Center for Biomedical Informatics) */ public class GenericClient extends BaseClient implements IGenericClient { + private static final String I18N_CANNOT_DETEMINE_RESOURCE_TYPE = "ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri"; private static final String I18N_INCOMPLETE_URI_FOR_READ = "ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead"; private static final String I18N_NO_VERSION_ID_FOR_VREAD = "ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread"; @@ -143,8 +80,7 @@ public class GenericClient extends BaseClient implements IGenericClient { private boolean myLogRequestAndResponse; /** - * For now, this is a part of the internal API of HAPI - Use with caution as - * this method may change! + * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! */ public GenericClient(FhirContext theContext, IHttpClient theHttpClient, String theServerBase, RestfulClientFactory theFactory) { super(theHttpClient, theServerBase, theFactory); @@ -277,6 +213,7 @@ public class GenericClient extends BaseClient implements IGenericClient { // public Bundle search(UriDt url) { // return search(inferResourceClass(url), url); // } + @Override public void forceConformanceCheck() { super.forceConformanceCheck(); @@ -363,11 +300,11 @@ public class GenericClient extends BaseClient implements IGenericClient { // public T read(final Class theType, IdDt theId) { // return doReadOrVRead(theType, theId, false, null, null); // } + /** - * @deprecated Use {@link LoggingInterceptor} as a client interceptor - * registered to your client instead, as this provides much more - * fine-grained control over what is logged. This method will be removed at - * some point (deprecated in HAPI 1.6 - 2016-06-16) + * @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your + * client instead, as this provides much more fine-grained control over what is logged. This + * method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16) */ @Deprecated public boolean isLogRequestAndResponse() { @@ -466,8 +403,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } /** - * For now, this is a part of the internal API of HAPI - Use with caution as - * this method may change! + * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! */ public void setLastRequest(IHttpRequest theLastRequest) { myLastRequest = theLastRequest; @@ -982,9 +918,8 @@ public class GenericClient extends BaseClient implements IGenericClient { } } - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings({ "rawtypes", "unchecked" }) private class FetchConformanceInternal extends BaseClientExecutable implements IFetchConformanceUntyped, IFetchConformanceTyped { - private RuntimeResourceDefinition myType; @Override @@ -1006,7 +941,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({ "unchecked", "rawtypes" }) private class GetPageInternal extends BaseClientExecutable, Object> implements IGetPageTyped { private Class myBundleType; @@ -1197,7 +1132,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({ "unchecked", "rawtypes" }) private final class LoadPageInternal implements IGetPage, IGetPageUntyped { private static final String PREV = "prev"; @@ -1388,7 +1323,6 @@ public class GenericClient extends BaseClient implements IGenericClient { } private enum MetaOperation { - ADD, DELETE, GET } @@ -1531,26 +1465,34 @@ public class GenericClient extends BaseClient implements IGenericClient { BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet); - ResourceResponseHandler handler = new ResourceResponseHandler(); - handler.setPreferResponseTypes(getPreferResponseTypes(myType)); + if (myReturnResourceType != null) { + ResourceResponseHandler handler; + handler = new ResourceResponseHandler(myReturnResourceType); + Object retVal = invoke(null, handler, invocation); + return retVal; + } else { + ResourceResponseHandler handler; + handler = new ResourceResponseHandler(); + handler.setPreferResponseTypes(getPreferResponseTypes(myType)); - Object retVal = invoke(null, handler, invocation); - if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { - return retVal; - } else { - RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); - IBaseResource parameters = def.newInstance(); + Object retVal = invoke(null, handler, invocation); + if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { + return retVal; + } else { + RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); + IBaseResource parameters = def.newInstance(); - BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); - IBase parameter = paramChildElem.newInstance(); - paramChild.getMutator().addValue(parameters, parameter); + BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); + BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); + IBase parameter = paramChildElem.newInstance(); + paramChild.getMutator().addValue(parameters, parameter); - BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); - resourceElem.getMutator().addValue(parameter, (IBase) retVal); + BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); + resourceElem.getMutator().addValue(parameter, (IBase) retVal); - return parameters; - } + return parameters; + } + } } } @@ -1622,7 +1564,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return this; } - @SuppressWarnings({"unchecked"}) + @SuppressWarnings({ "unchecked" }) @Override public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) { Validate.notNull(theParameters, "theParameters can not be null"); @@ -2447,6 +2389,7 @@ public class GenericClient extends BaseClient implements IGenericClient { return this; } + @Override public IPatchWithQueryTyped where(ICriterion theCriterion) { myCriterionList.add((ICriterionInternal) theCriterion); @@ -2623,7 +2566,6 @@ public class GenericClient extends BaseClient implements IGenericClient { } private class ValidateInternal extends BaseClientExecutable implements IValidate, IValidateUntyped { - private IBaseResource myResource; @Override @@ -2664,2469 +2606,5 @@ public class GenericClient extends BaseClient implements IGenericClient { } } -======= - private static final String I18N_CANNOT_DETEMINE_RESOURCE_TYPE = "ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri"; - private static final String I18N_INCOMPLETE_URI_FOR_READ = "ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead"; - private static final String I18N_NO_VERSION_ID_FOR_VREAD = "ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread"; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class); - private FhirContext myContext; - private IHttpRequest myLastRequest; - private boolean myLogRequestAndResponse; - - /** - * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! - */ - public GenericClient(FhirContext theContext, IHttpClient theHttpClient, String theServerBase, RestfulClientFactory theFactory) { - super(theHttpClient, theServerBase, theFactory); - myContext = theContext; - } - - @Override - public IBaseConformance conformance() { - if (myContext.getVersion().getVersion().isRi()) { - throw new IllegalArgumentException("Must call fetchConformance() instead of conformance() for RI/STU3+ structures"); - } - - HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - @SuppressWarnings("unchecked") - Class conformance = (Class) myContext.getResourceDefinition("Conformance").getImplementingClass(); - - ResourceResponseHandler binding = new ResourceResponseHandler(conformance); - IBaseConformance resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public ICreate create() { - return new CreateInternal(); - } - - @Override - public MethodOutcome create(IBaseResource theResource) { - BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(theResource, myContext); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - - } - - @Override - public IDelete delete() { - return new DeleteInternal(); - } - - @Override - public MethodOutcome delete(final Class theType, IdDt theId) { - HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), theId.withResourceType(toResourceName(theType))); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - final String resourceName = myContext.getResourceDefinition(theType).getName(); - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public MethodOutcome delete(Class theType, String theId) { - return delete(theType, new IdDt(theId)); - } - - private T doReadOrVRead(final Class theType, IIdType theId, boolean theVRead, ICallable theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint, - SummaryEnum theSummary, EncodingEnum theEncoding, Set theSubsetElements) { - String resName = toResourceName(theType); - IIdType id = theId; - if (!id.hasBaseUrl()) { - id = new IdDt(resName, id.getIdPart(), id.getVersionIdPart()); - } - - HttpGetClientInvocation invocation; - if (id.hasBaseUrl()) { - if (theVRead) { - invocation = ReadMethodBinding.createAbsoluteVReadInvocation(getFhirContext(), id); - } else { - invocation = ReadMethodBinding.createAbsoluteReadInvocation(getFhirContext(), id); - } - } else { - if (theVRead) { - invocation = ReadMethodBinding.createVReadInvocation(getFhirContext(), id, resName); - } else { - invocation = ReadMethodBinding.createReadInvocation(getFhirContext(), id, resName); - } - } - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - if (theIfVersionMatches != null) { - invocation.addHeader(Constants.HEADER_IF_NONE_MATCH, '"' + theIfVersionMatches + '"'); - } - - boolean allowHtmlResponse = (theSummary == SummaryEnum.TEXT) || (theSummary == null && getSummary() == SummaryEnum.TEXT); - ResourceResponseHandler binding = new ResourceResponseHandler(theType, (Class) null, id, allowHtmlResponse); - - if (theNotModifiedHandler == null) { - return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); - } else { - try { - return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements); - } catch (NotModifiedException e) { - return theNotModifiedHandler.call(); - } - } - - } - - @Override - public IFetchConformanceUntyped fetchConformance() { - return new FetchConformanceInternal(); - } - - // public IResource read(UriDt url) { - // return read(inferResourceClass(url), url); - // } - // - // @SuppressWarnings("unchecked") - // public T read(final Class theType, UriDt url) { - // return (T) invoke(theType, url, new ResourceResponseHandler(theType)); - // } - // - // public Bundle search(UriDt url) { - // return search(inferResourceClass(url), url); - // } - - @Override - public void forceConformanceCheck() { - super.forceConformanceCheck(); - } - - @Override - public FhirContext getFhirContext() { - return myContext; - } - - public IHttpRequest getLastRequest() { - return myLastRequest; - } - - protected String getPreferredId(IBaseResource theResource, String theId) { - if (isNotBlank(theId)) { - return theId; - } - return theResource.getIdElement().getIdPart(); - } - - @Override - public IGetTags getTags() { - return new GetTagsInternal(); - } - - @Override - public IHistory history() { - return new HistoryInternal(); - } - - @Override - public Bundle history(final Class theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit) { - String resourceName = theType != null ? toResourceName(theType) : null; - String id = theIdDt != null && theIdDt.isEmpty() == false ? theIdDt.getValue() : null; - HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, theSince, theLimit); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - BundleResponseHandler binding = new BundleResponseHandler(theType); - Bundle resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - - } - - @Override - public Bundle history(Class theType, String theId, DateTimeDt theSince, Integer theLimit) { - return history(theType, new IdDt(theId), theSince, theLimit); - } - - private Class inferResourceClass(UriDt theUrl) { - String urlString = theUrl.getValueAsString(); - int i = urlString.indexOf('?'); - - if (i >= 0) { - urlString = urlString.substring(0, i); - } - - i = urlString.indexOf("://"); - - if (i >= 0) { - urlString = urlString.substring(i + 3); - } - - String[] pcs = urlString.split("\\/"); - - for (i = pcs.length - 1; i >= 0; i--) { - String s = pcs[i].trim(); - - if (!s.isEmpty()) { - RuntimeResourceDefinition def = myContext.getResourceDefinition(s); - if (def != null) { - return def.getImplementingClass(); - } - } - } - - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); - - } - - // @Override - // public T read(final Class theType, IdDt theId) { - // return doReadOrVRead(theType, theId, false, null, null); - // } - - /** - * @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your - * client instead, as this provides much more fine-grained control over what is logged. This - * method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16) - */ - @Deprecated - public boolean isLogRequestAndResponse() { - return myLogRequestAndResponse; - } - - @Override - public IGetPage loadPage() { - return new LoadPageInternal(); - } - - @Override - public IMeta meta() { - if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { - throw new IllegalStateException("Can not call $meta operations on a DSTU1 client"); - } - return new MetaInternal(); - } - - @Override - public IOperation operation() { - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) == false) { - 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()); - } - return new OperationInternal(); - } - - @Override - public IRead read() { - return new ReadInternal(); - } - - @Override - public T read(Class theType, String theId) { - return read(theType, new IdDt(theId)); - } - - @Override - public T read(final Class theType, UriDt theUrl) { - IdDt id = theUrl instanceof IdDt ? ((IdDt) theUrl) : new IdDt(theUrl); - return doReadOrVRead(theType, id, false, null, null, false, null, null, null); - } - - @Override - public IBaseResource read(UriDt theUrl) { - IdDt id = new IdDt(theUrl); - String resourceType = id.getResourceType(); - if (isBlank(resourceType)) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, theUrl.getValueAsString())); - } - RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType); - if (def == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString())); - } - return read(def.getImplementingClass(), id); - } - - @Override - public IUntypedQuery search() { - return new SearchInternal(); - } - - @Override - public Bundle search(final Class theType, Map> theParams) { - LinkedHashMap> params = new LinkedHashMap>(); - for (Entry> nextEntry : theParams.entrySet()) { - ArrayList valueList = new ArrayList(); - String qualifier = null; - for (IQueryParameterType nextValue : nextEntry.getValue()) { - valueList.add(nextValue.getValueAsQueryToken(myContext)); - qualifier = nextValue.getQueryParameterQualifier(); - } - qualifier = StringUtils.defaultString(qualifier); - params.put(nextEntry.getKey() + qualifier, valueList); - } - - BaseHttpClientInvocation invocation = SearchMethodBinding.createSearchInvocation(myContext, toResourceName(theType), params, null, null, null); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - BundleResponseHandler binding = new BundleResponseHandler(theType); - Bundle resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public Bundle search(final Class theType, UriDt theUrl) { - BaseHttpClientInvocation invocation = new HttpGetClientInvocation(getFhirContext(), theUrl.getValueAsString()); - return invokeClient(myContext, new BundleResponseHandler(theType), invocation); - } - - @Override - public Bundle search(UriDt theUrl) { - return search(inferResourceClass(theUrl), theUrl); - } - - /** - * For now, this is a part of the internal API of HAPI - Use with caution as this method may change! - */ - public void setLastRequest(IHttpRequest theLastRequest) { - myLastRequest = theLastRequest; - } - - @Override - public void setLogRequestAndResponse(boolean theLogRequestAndResponse) { - myLogRequestAndResponse = theLogRequestAndResponse; - } - - private String toResourceName(Class theType) { - return myContext.getResourceDefinition(theType).getName(); - } - - @Override - public ITransaction transaction() { - return new TransactionInternal(); - } - - @Override - public List transaction(List theResources) { - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - Bundle resp = invokeClient(myContext, new BundleResponseHandler(null), invocation, myLogRequestAndResponse); - - return new ArrayList(resp.toListOfResources()); - } - - @Override - public IPatch patch() { - return new PatchInternal(); - } - - @Override - public IUpdate update() { - return new UpdateInternal(); - } - - @Override - public MethodOutcome update(IdDt theIdDt, IBaseResource theResource) { - BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext); - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public MethodOutcome update(String theId, IBaseResource theResource) { - return update(new IdDt(theId), theResource); - } - - @Override - public IValidate validate() { - return new ValidateInternal(); - } - - @Override - public MethodOutcome validate(IBaseResource theResource) { - BaseHttpClientInvocation invocation; - if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { - invocation = ValidateMethodBindingDstu1.createValidateInvocation(theResource, null, myContext); - } else { - invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, theResource); - } - - if (isKeepResponses()) { - myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint()); - } - - RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName); - MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); - return resp; - } - - @Override - public T vread(final Class theType, IdDt theId) { - if (theId.hasVersionIdPart() == false) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue())); - } - return doReadOrVRead(theType, theId, true, null, null, false, null, null, null); - } - - /* also deprecated in interface */ - @Deprecated - @Override - public T vread(final Class theType, IdDt theId, IdDt theVersionId) { - return vread(theType, theId.withVersion(theVersionId.getIdPart())); - } - - @Override - public T vread(Class theType, String theId, String theVersionId) { - IdDt resId = new IdDt(toResourceName(theType), theId, theVersionId); - return vread(theType, resId); - } - - private static void addParam(Map> params, String parameterName, String parameterValue) { - if (!params.containsKey(parameterName)) { - params.put(parameterName, new ArrayList()); - } - params.get(parameterName).add(parameterValue); - } - - private static void addPreferHeader(PreferReturnEnum thePrefer, BaseHttpClientInvocation theInvocation) { - if (thePrefer != null) { - theInvocation.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + thePrefer.getHeaderValue()); - } - } - - private static String validateAndEscapeConditionalUrl(String theSearchUrl) { - Validate.notBlank(theSearchUrl, "Conditional URL can not be blank/null"); - StringBuilder b = new StringBuilder(); - boolean haveHadQuestionMark = false; - for (int i = 0; i < theSearchUrl.length(); i++) { - char nextChar = theSearchUrl.charAt(i); - if (!haveHadQuestionMark) { - if (nextChar == '?') { - haveHadQuestionMark = true; - } else if (!Character.isLetter(nextChar)) { - throw new IllegalArgumentException("Conditional URL must be in the format \"[ResourceType]?[Params]\" and must not have a base URL - Found: " + theSearchUrl); - } - b.append(nextChar); - } else { - switch (nextChar) { - case '|': - case '?': - case '$': - case ':': - b.append(UrlUtil.escape(Character.toString(nextChar))); - break; - default: - b.append(nextChar); - break; - } - } - } - return b.toString(); - } - - private abstract class BaseClientExecutable, Y> implements IClientExecutable { - - protected EncodingEnum myParamEncoding; - - private List> myPreferResponseTypes; - - protected Boolean myPrettyPrint; - - private boolean myQueryLogRequestAndResponse; - - private HashSet mySubsetElements; - - protected SummaryEnum mySummaryMode; - - @SuppressWarnings("unchecked") - @Override - public T andLogRequestAndResponse(boolean theLogRequestAndResponse) { - myQueryLogRequestAndResponse = theLogRequestAndResponse; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T elementsSubset(String... theElements) { - if (theElements != null && theElements.length > 0) { - mySubsetElements = new HashSet(Arrays.asList(theElements)); - } else { - mySubsetElements = null; - } - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T encodedJson() { - myParamEncoding = EncodingEnum.JSON; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T encodedXml() { - myParamEncoding = EncodingEnum.XML; - return (T) this; - } - - protected EncodingEnum getParamEncoding() { - return myParamEncoding; - } - - public List> getPreferResponseTypes() { - return myPreferResponseTypes; - } - - public List> getPreferResponseTypes(Class theDefault) { - if (myPreferResponseTypes != null) { - return myPreferResponseTypes; - } else { - return toTypeList(theDefault); - } - } - - protected HashSet getSubsetElements() { - return mySubsetElements; - } - - protected Z invoke(Map> theParams, IClientResponseHandler theHandler, BaseHttpClientInvocation theInvocation) { - // if (myParamEncoding != null) { - // theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType())); - // } - // - // if (myPrettyPrint != null) { - // theParams.put(Constants.PARAM_PRETTY, Collections.singletonList(myPrettyPrint.toString())); - // } - - if (isKeepResponses()) { - myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint); - } - - Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse, mySummaryMode, mySubsetElements); - return resp; - } - - protected IBaseResource parseResourceBody(String theResourceBody) { - EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody); - if (encoding == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); - } - return encoding.newParser(myContext).parseResource(theResourceBody); - } - - @SuppressWarnings("unchecked") - @Override - public T preferResponseType(Class theClass) { - myPreferResponseTypes = null; - if (theClass != null) { - myPreferResponseTypes = new ArrayList>(); - myPreferResponseTypes.add(theClass); - } - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T preferResponseTypes(List> theClass) { - myPreferResponseTypes = theClass; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T prettyPrint() { - myPrettyPrint = true; - return (T) this; - } - - @SuppressWarnings("unchecked") - @Override - public T summaryMode(SummaryEnum theSummary) { - mySummaryMode = theSummary; - return ((T) this); - } - - } - - private final class BundleResponseHandler implements IClientResponseHandler { - - private Class myType; - - public BundleResponseHandler(Class theType) { - myType = theType; - } - - @Override - public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); - } - IParser parser = respType.newParser(myContext); - return parser.parseBundle(myType, theResponseReader); - } - } - - private class CreateInternal extends BaseClientExecutable implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped { - - private CriterionList myCriterionList; - private String myId; - private PreferReturnEnum myPrefer; - private IBaseResource myResource; - private String myResourceBody; - private String mySearchUrl; - - @Override - public ICreateWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public ICreateWithQuery conditional() { - myCriterionList = new CriterionList(); - return this; - } - - @Override - public ICreateTyped conditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public MethodOutcome execute() { - if (myResource == null) { - myResource = parseResourceBody(myResourceBody); - } - myId = getPreferredId(myResource, myId); - - // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding - if (getParamEncoding() != null) { - myResourceBody = null; - } - - BaseHttpClientInvocation invocation; - if (mySearchUrl != null) { - invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, mySearchUrl); - } else if (myCriterionList != null) { - invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, myCriterionList.toParamList()); - } else { - invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext); - } - - addPreferHeader(myPrefer, invocation); - - RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer); - - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - - } - - @Override - public ICreateTyped prefer(PreferReturnEnum theReturn) { - myPrefer = theReturn; - return this; - } - - @Override - public ICreateTyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "Resource can not be null"); - myResource = theResource; - return this; - } - - @Override - public ICreateTyped resource(String theResourceBody) { - Validate.notBlank(theResourceBody, "Body can not be null or blank"); - myResourceBody = theResourceBody; - return this; - } - - @Override - public ICreateWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public CreateInternal withId(IdDt theId) { - myId = theId.getIdPart(); - return this; - } - - @Override - public CreateInternal withId(String theId) { - myId = theId; - return this; - } - - } - - private class CriterionList extends ArrayList { - - private static final long serialVersionUID = 1L; - - public void populateParamList(Map> theParams) { - for (ICriterionInternal next : this) { - String parameterName = next.getParameterName(); - String parameterValue = next.getParameterValue(myContext); - if (isNotBlank(parameterValue)) { - addParam(theParams, parameterName, parameterValue); - } - } - } - - public Map> toParamList() { - LinkedHashMap> retVal = new LinkedHashMap>(); - populateParamList(retVal); - return retVal; - } - - } - - private class DeleteInternal extends BaseClientExecutable implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped { - - private CriterionList myCriterionList; - private IIdType myId; - private String myResourceType; - private String mySearchUrl; - - @Override - public IDeleteWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IBaseOperationOutcome execute() { - HttpDeleteClientInvocation invocation; - if (myId != null) { - invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myId); - } else if (myCriterionList != null) { - Map> params = myCriterionList.toParamList(); - invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myResourceType, params); - } else { - invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), mySearchUrl); - } - OperationOutcomeResponseHandler binding = new OperationOutcomeResponseHandler(); - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - } - - @Override - public IDeleteTyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "theResource can not be null"); - IIdType id = theResource.getIdElement(); - Validate.notNull(id, "theResource.getIdElement() can not be null"); - if (id.hasResourceType() == false || id.hasIdPart() == false) { - throw new IllegalArgumentException("theResource.getId() must contain a resource type and logical ID at a minimum (e.g. Patient/1234), found: " + id.getValue()); - } - myId = id; - return this; - } - - @Override - public IDeleteTyped resourceById(IIdType theId) { - Validate.notNull(theId, "theId can not be null"); - if (theId.hasResourceType() == false || theId.hasIdPart() == false) { - throw new IllegalArgumentException("theId must contain a resource type and logical ID at a minimum (e.g. Patient/1234)found: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IDeleteTyped resourceById(String theResourceType, String theLogicalId) { - Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); - if (myContext.getResourceDefinition(theResourceType) == null) { - throw new IllegalArgumentException("Unknown resource type"); - } - Validate.notBlank(theLogicalId, "theLogicalId can not be blank/null"); - if (theLogicalId.contains("/")) { - throw new IllegalArgumentException("LogicalId can not contain '/' (should only be the logical ID portion, not a qualified ID)"); - } - myId = new IdDt(theResourceType, theLogicalId); - return this; - } - - @Override - public IDeleteWithQuery resourceConditionalByType(Class theResourceType) { - Validate.notNull(theResourceType, "theResourceType can not be null"); - myCriterionList = new CriterionList(); - myResourceType = myContext.getResourceDefinition(theResourceType).getName(); - return this; - } - - @Override - public IDeleteWithQuery resourceConditionalByType(String theResourceType) { - Validate.notBlank(theResourceType, "theResourceType can not be blank/null"); - if (myContext.getResourceDefinition(theResourceType) == null) { - throw new IllegalArgumentException("Unknown resource type: " + theResourceType); - } - myResourceType = theResourceType; - myCriterionList = new CriterionList(); - return this; - } - - @Override - public IDeleteTyped resourceConditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public IDeleteWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private class FetchConformanceInternal extends BaseClientExecutable implements IFetchConformanceUntyped, IFetchConformanceTyped { - private RuntimeResourceDefinition myType; - - @Override - public Object execute() { - ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass()); - HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); - return super.invoke(null, binding, invocation); - } - - @Override - public IFetchConformanceTyped ofType(Class theResourceType) { - Validate.notNull(theResourceType, "theResourceType must not be null"); - myType = myContext.getResourceDefinition(theResourceType); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); - } - return this; - } - - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private class GetPageInternal extends BaseClientExecutable, Object> implements IGetPageTyped { - - private Class myBundleType; - private String myUrl; - - public GetPageInternal(String theUrl) { - myUrl = theUrl; - } - - public GetPageInternal(String theUrl, Class theBundleType) { - myUrl = theUrl; - myBundleType = theBundleType; - } - - @Override - public Object execute() { - IClientResponseHandler binding; - if (myBundleType == null) { - binding = new BundleResponseHandler(null); - } else { - binding = new ResourceResponseHandler(myBundleType, getPreferResponseTypes()); - } - HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myContext, myUrl); - - Map> params = null; - return invoke(params, binding, invocation); - } - - } - - private class GetTagsInternal extends BaseClientExecutable implements IGetTags { - - private String myId; - private String myResourceName; - private String myVersionId; - - @Override - public TagList execute() { - - Map> params = new LinkedHashMap>(); - Map> initial = createExtraParams(); - if (initial != null) { - params.putAll(initial); - } - - TagListResponseHandler binding = new TagListResponseHandler(); - List urlFragments = new ArrayList(); - if (isNotBlank(myResourceName)) { - urlFragments.add(myResourceName); - if (isNotBlank(myId)) { - urlFragments.add(myId); - if (isNotBlank(myVersionId)) { - urlFragments.add(Constants.PARAM_HISTORY); - urlFragments.add(myVersionId); - } - } - } - urlFragments.add(Constants.PARAM_TAGS); - - HttpGetClientInvocation invocation = new HttpGetClientInvocation(myContext, params, urlFragments); - - return invoke(params, binding, invocation); - - } - - @Override - public IGetTags forResource(Class theClass) { - setResourceClass(theClass); - return this; - } - - @Override - public IGetTags forResource(Class theClass, String theId) { - setResourceClass(theClass); - myId = theId; - return this; - } - - @Override - public IGetTags forResource(Class theClass, String theId, String theVersionId) { - setResourceClass(theClass); - myId = theId; - myVersionId = theVersionId; - return this; - } - - private void setResourceClass(Class theClass) { - if (theClass != null) { - myResourceName = myContext.getResourceDefinition(theClass).getName(); - } else { - myResourceName = null; - } - } - - } - - @SuppressWarnings("rawtypes") - private class HistoryInternal extends BaseClientExecutable implements IHistory, IHistoryUntyped, IHistoryTyped { - - private Integer myCount; - private IIdType myId; - private Class myReturnType; - private IPrimitiveType mySince; - private Class myType; - - @SuppressWarnings("unchecked") - @Override - public IHistoryTyped andReturnBundle(Class theType) { - myReturnType = theType; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IHistoryTyped andReturnDstu1Bundle() { - return this; - } - - @Override - public IHistoryTyped count(Integer theCount) { - myCount = theCount; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public Object execute() { - String resourceName; - String id; - if (myType != null) { - resourceName = myContext.getResourceDefinition(myType).getName(); - id = null; - } else if (myId != null) { - resourceName = myId.getResourceType(); - id = myId.getIdPart(); - } else { - resourceName = null; - id = null; - } - - HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, mySince, myCount); - - IClientResponseHandler handler; - if (myReturnType != null) { - handler = new ResourceResponseHandler(myReturnType, getPreferResponseTypes(myType)); - } else { - handler = new BundleResponseHandler(null); - } - - return invoke(null, handler, invocation); - } - - @Override - public IHistoryUntyped onInstance(IIdType theId) { - if (theId.hasResourceType() == false) { - throw new IllegalArgumentException("Resource ID does not have a resource type: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IHistoryUntyped onServer() { - return this; - } - - @Override - public IHistoryUntyped onType(Class theResourceType) { - myType = theResourceType; - return this; - } - - @Override - public IHistoryTyped since(Date theCutoff) { - if (theCutoff != null) { - mySince = new InstantDt(theCutoff); - } else { - mySince = null; - } - return this; - } - - @Override - public IHistoryTyped since(IPrimitiveType theCutoff) { - mySince = theCutoff; - return this; - } - - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private final class LoadPageInternal implements IGetPage, IGetPageUntyped { - - private static final String PREV = "prev"; - private static final String PREVIOUS = "previous"; - private String myPageUrl; - - @Override - public IGetPageTyped andReturnBundle(Class theBundleType) { - Validate.notNull(theBundleType, "theBundleType must not be null"); - return new GetPageInternal(myPageUrl, theBundleType); - } - - @Override - public IGetPageTyped andReturnDstu1Bundle() { - return new GetPageInternal(myPageUrl); - } - - @Override - public IGetPageUntyped byUrl(String thePageUrl) { - if (isBlank(thePageUrl)) { - throw new IllegalArgumentException("thePagingUrl must not be blank or null"); - } - myPageUrl = thePageUrl; - return this; - } - - @Override - public IGetPageTyped next(Bundle theBundle) { - return new GetPageInternal(theBundle.getLinkNext().getValue()); - } - - @Override - public IGetPageTyped next(T theBundle) { - return nextOrPrevious("next", theBundle); - } - - private IGetPageTyped nextOrPrevious(String theWantRel, T theBundle) { - RuntimeResourceDefinition def = myContext.getResourceDefinition(theBundle); - List links = def.getChildByName("link").getAccessor().getValues(theBundle); - if (links == null || links.isEmpty()) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); - } - for (IBase nextLink : links) { - BaseRuntimeElementCompositeDefinition linkDef = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(nextLink.getClass()); - List rel = linkDef.getChildByName("relation").getAccessor().getValues(nextLink); - if (rel == null || rel.isEmpty()) { - continue; - } - String relation = ((IPrimitiveType) rel.get(0)).getValueAsString(); - if (theWantRel.equals(relation) || (theWantRel == PREVIOUS && PREV.equals(relation))) { - List urls = linkDef.getChildByName("url").getAccessor().getValues(nextLink); - if (urls == null || urls.isEmpty()) { - continue; - } - String url = ((IPrimitiveType) urls.get(0)).getValueAsString(); - if (isBlank(url)) { - continue; - } - return (IGetPageTyped) byUrl(url).andReturnBundle(theBundle.getClass()); - } - } - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "noPagingLinkFoundInBundle", theWantRel)); - } - - @Override - public IGetPageTyped previous(Bundle theBundle) { - return new GetPageInternal(theBundle.getLinkPrevious().getValue()); - } - - @Override - public IGetPageTyped previous(T theBundle) { - return nextOrPrevious(PREVIOUS, theBundle); - } - - @Override - public IGetPageTyped url(String thePageUrl) { - return new GetPageInternal(thePageUrl); - } - - } - - @SuppressWarnings("rawtypes") - private class MetaInternal extends BaseClientExecutable implements IMeta, IMetaAddOrDeleteUnsourced, IMetaGetUnsourced, IMetaAddOrDeleteSourced { - - private IIdType myId; - private IBaseMetaType myMeta; - private Class myMetaType; - private String myOnType; - private MetaOperation myOperation; - - @Override - public IMetaAddOrDeleteUnsourced add() { - myOperation = MetaOperation.ADD; - return this; - } - - @Override - public IMetaAddOrDeleteUnsourced delete() { - myOperation = MetaOperation.DELETE; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public Object execute() { - - BaseHttpClientInvocation invocation = null; - - IBaseParameters parameters = ParametersUtil.newInstance(myContext); - switch (myOperation) { - case ADD: - ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta"); - invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-add", parameters, false); - break; - case DELETE: - ParametersUtil.addParameterToParameters(myContext, parameters, myMeta, "meta"); - invocation = OperationMethodBinding.createOperationInvocation(myContext, myId.getResourceType(), myId.getIdPart(), "$meta-delete", parameters, false); - break; - case GET: - if (myId != null) { - invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, myId.getIdPart(), "$meta", parameters, true); - } else if (myOnType != null) { - invocation = OperationMethodBinding.createOperationInvocation(myContext, myOnType, null, "$meta", parameters, true); - } else { - invocation = OperationMethodBinding.createOperationInvocation(myContext, null, null, "$meta", parameters, true); - } - break; - } - - // Should not happen - if (invocation == null) { - throw new IllegalStateException(); - } - - IClientResponseHandler handler; - handler = new MetaParametersResponseHandler(myMetaType); - return invoke(null, handler, invocation); - } - - @Override - public IClientExecutable fromResource(IIdType theId) { - setIdInternal(theId); - return this; - } - - @Override - public IClientExecutable fromServer() { - return this; - } - - @Override - public IClientExecutable fromType(String theResourceName) { - Validate.notBlank(theResourceName, "theResourceName must not be blank"); - myOnType = theResourceName; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IMetaGetUnsourced get(Class theType) { - myMetaType = theType; - myOperation = MetaOperation.GET; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IClientExecutable, T> meta(T theMeta) { - Validate.notNull(theMeta, "theMeta must not be null"); - myMeta = theMeta; - myMetaType = myMeta.getClass(); - return this; - } - - @Override - public IMetaAddOrDeleteSourced onResource(IIdType theId) { - setIdInternal(theId); - return this; - } - - private void setIdInternal(IIdType theId) { - Validate.notBlank(theId.getResourceType(), "theId must contain a resource type"); - Validate.notBlank(theId.getIdPart(), "theId must contain an ID part"); - myOnType = theId.getResourceType(); - myId = theId; - } - - } - - private enum MetaOperation { - ADD, DELETE, GET - } - - private final class MetaParametersResponseHandler implements IClientResponseHandler { - - private Class myType; - - public MetaParametersResponseHandler(Class theMetaType) { - myType = theMetaType; - } - - @SuppressWarnings("unchecked") - @Override - public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); - } - IParser parser = respType.newParser(myContext); - RuntimeResourceDefinition type = myContext.getResourceDefinition("Parameters"); - IBaseResource retVal = parser.parseResource(type.getImplementingClass(), theResponseReader); - - BaseRuntimeChildDefinition paramChild = type.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); - List parameter = paramChild.getAccessor().getValues(retVal); - if (parameter == null || parameter.isEmpty()) { - return (T) myContext.getElementDefinition(myType).newInstance(); - } - IBase param = parameter.get(0); - - List meta = paramChildElem.getChildByName("value[x]").getAccessor().getValues(param); - if (meta.isEmpty()) { - return (T) myContext.getElementDefinition(myType).newInstance(); - } - return (T) meta.get(0); - - } - } - - @SuppressWarnings("rawtypes") - private class OperationInternal extends BaseClientExecutable implements IOperation, IOperationUnnamed, IOperationUntyped, IOperationUntypedWithInput, IOperationUntypedWithInputAndPartialOutput { - - private IIdType myId; - private String myOperationName; - private IBaseParameters myParameters; - private RuntimeResourceDefinition myParametersDef; - private Class myType; - private boolean myUseHttpGet; - private Class myReturnResourceType; - - @SuppressWarnings("unchecked") - private void addParam(String theName, IBase theValue) { - BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition parameterElem = (BaseRuntimeElementCompositeDefinition) parameterChild.getChildByName("parameter"); - - IBase parameter = parameterElem.newInstance(); - parameterChild.getMutator().addValue(myParameters, parameter); - - IPrimitiveType name = (IPrimitiveType) myContext.getElementDefinition("string").newInstance(); - name.setValue(theName); - parameterElem.getChildByName("name").getMutator().setValue(parameter, name); - - if (theValue instanceof IBaseDatatype) { - BaseRuntimeElementDefinition datatypeDef = myContext.getElementDefinition(theValue.getClass()); - if (datatypeDef instanceof IRuntimeDatatypeDefinition) { - Class profileOf = ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf(); - if (profileOf != null) { - datatypeDef = myContext.getElementDefinition(profileOf); - } - } - String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName()); - BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName); - childByName.getMutator().setValue(parameter, theValue); - } else if (theValue instanceof IBaseResource) { - parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue); - } else { - throw new IllegalArgumentException("Don't know how to handle parameter of type " + theValue.getClass()); - } - } - - private void addParam(String theName, IQueryParameterType theValue) { - IPrimitiveType stringType = ParametersUtil.createString(myContext, theValue.getValueAsQueryToken(myContext)); - addParam(theName, stringType); - } - - @Override - public IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue) { - Validate.notEmpty(theName, "theName must not be null"); - Validate.notNull(theValue, "theValue must not be null"); - addParam(theName, theValue); - return this; - } - - @Override - public IOperationUntypedWithInputAndPartialOutput andSearchParameter(String theName, IQueryParameterType theValue) { - addParam(theName, theValue); - - return this; - } - - @SuppressWarnings("unchecked") - @Override - public Object execute() { - String resourceName; - String id; - if (myType != null) { - resourceName = myContext.getResourceDefinition(myType).getName(); - id = null; - } else if (myId != null) { - resourceName = myId.getResourceType(); - id = myId.getIdPart(); - } else { - resourceName = null; - id = null; - } - - BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet); - - if (myReturnResourceType != null) { - ResourceResponseHandler handler; - handler = new ResourceResponseHandler(myReturnResourceType); - Object retVal = invoke(null, handler, invocation); - return retVal; - } else { - ResourceResponseHandler handler; - handler = new ResourceResponseHandler(); - handler.setPreferResponseTypes(getPreferResponseTypes(myType)); - - Object retVal = invoke(null, handler, invocation); - if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) { - return retVal; - } else { - RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); - IBaseResource parameters = def.newInstance(); - - BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); - BaseRuntimeElementCompositeDefinition paramChildElem = (BaseRuntimeElementCompositeDefinition) paramChild.getChildByName("parameter"); - IBase parameter = paramChildElem.newInstance(); - paramChild.getMutator().addValue(parameters, parameter); - - BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); - resourceElem.getMutator().addValue(parameter, (IBase) retVal); - - return parameters; - } - } - } - - @Override - public IOperationUntyped named(String theName) { - Validate.notBlank(theName, "theName can not be null"); - myOperationName = theName; - return this; - } - - @Override - public IOperationUnnamed onInstance(IIdType theId) { - myId = theId; - return this; - } - - @Override - public IOperationUnnamed onServer() { - return this; - } - - @Override - public IOperationUnnamed onType(Class theResourceType) { - myType = theResourceType; - return this; - } - - @Override - public IOperationUntypedWithInput useHttpGet() { - myUseHttpGet = true; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IOperationUntypedWithInput withNoParameters(Class theOutputParameterType) { - Validate.notNull(theOutputParameterType, "theOutputParameterType may not be null"); - RuntimeResourceDefinition def = myContext.getResourceDefinition(theOutputParameterType); - if (def == null) { - throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName()); - } - if (!"Parameters".equals(def.getName())) { - throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName() - + " is a resource named: " + def.getName()); - } - myParameters = (IBaseParameters) def.newInstance(); - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IOperationUntypedWithInputAndPartialOutput withParameter(Class theParameterType, String theName, IBase theValue) { - Validate.notNull(theParameterType, "theParameterType must not be null"); - Validate.notEmpty(theName, "theName must not be null"); - Validate.notNull(theValue, "theValue must not be null"); - - myParametersDef = myContext.getResourceDefinition(theParameterType); - myParameters = (IBaseParameters) myParametersDef.newInstance(); - - addParam(theName, theValue); - - return this; - } - - @SuppressWarnings({ "unchecked" }) - @Override - public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) { - Validate.notNull(theParameters, "theParameters can not be null"); - myParameters = theParameters; - return this; - } - - @SuppressWarnings("unchecked") - @Override - public IOperationUntypedWithInputAndPartialOutput withSearchParameter(Class theParameterType, String theName, IQueryParameterType theValue) { - Validate.notNull(theParameterType, "theParameterType must not be null"); - Validate.notEmpty(theName, "theName must not be null"); - Validate.notNull(theValue, "theValue must not be null"); - - myParametersDef = myContext.getResourceDefinition(theParameterType); - myParameters = (IBaseParameters) myParametersDef.newInstance(); - - addParam(theName, theValue); - - return this; - } - - @Override - public IOperationUntypedWithInput returnResourceType(Class theReturnType) { - Validate.notNull(theReturnType, "theReturnType must not be null"); - Validate.isTrue(IBaseResource.class.isAssignableFrom(theReturnType), "theReturnType must be a class which extends from IBaseResource"); - myReturnResourceType = theReturnType; - return this; - } - - } - - private final class OperationOutcomeResponseHandler implements IClientResponseHandler { - - @Override - public IBaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) - throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - return null; - } - IParser parser = respType.newParser(myContext); - IBaseOperationOutcome retVal; - try { - // TODO: handle if something else than OO comes back - retVal = (IBaseOperationOutcome) parser.parseResource(theResponseReader); - } catch (DataFormatException e) { - ourLog.warn("Failed to parse OperationOutcome response", e); - return null; - } - MethodUtil.parseClientRequestResourceHeaders(null, theHeaders, retVal); - - return retVal; - } - } - - private final class OutcomeResponseHandler implements IClientResponseHandler { - private PreferReturnEnum myPrefer; - private final String myResourceName; - - private OutcomeResponseHandler(String theResourceName) { - myResourceName = theResourceName; - } - - private OutcomeResponseHandler(String theResourceName, PreferReturnEnum thePrefer) { - this(theResourceName); - myPrefer = thePrefer; - } - - @Override - public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - MethodOutcome response = MethodUtil.process2xxResponse(myContext, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders); - if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) { - response.setCreated(true); - } - - if (myPrefer == PreferReturnEnum.REPRESENTATION) { - if (response.getResource() == null) { - if (response.getId() != null && isNotBlank(response.getId().getValue()) && response.getId().hasBaseUrl()) { - ourLog.info("Server did not return resource for Prefer-representation, going to fetch: {}", response.getId().getValue()); - IBaseResource resource = read().resource(response.getId().getResourceType()).withUrl(response.getId()).execute(); - response.setResource(resource); - } - } - } - - return response; - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private class ReadInternal extends BaseClientExecutable implements IRead, IReadTyped, IReadExecutable { - private IIdType myId; - private String myIfVersionMatches; - private ICallable myNotModifiedHandler; - private RuntimeResourceDefinition myType; - - @Override - public Object execute() {// AAA - if (myId.hasVersionIdPart()) { - return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); - } else { - return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements()); - } - } - - @Override - public IReadIfNoneMatch ifVersionMatches(String theVersion) { - myIfVersionMatches = theVersion; - return new IReadIfNoneMatch() { - - @Override - public IReadExecutable returnNull() { - myNotModifiedHandler = new ICallable() { - @Override - public Object call() { - return null; - } - }; - return ReadInternal.this; - } - - @Override - public IReadExecutable returnResource(final IBaseResource theInstance) { - myNotModifiedHandler = new ICallable() { - @Override - public Object call() { - return theInstance; - } - }; - return ReadInternal.this; - } - - @Override - public IReadExecutable throwNotModifiedException() { - myNotModifiedHandler = null; - return ReadInternal.this; - } - }; - } - - private void processUrl() { - String resourceType = myId.getResourceType(); - if (isBlank(resourceType)) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, myId)); - } - myType = myContext.getResourceDefinition(resourceType); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, myId)); - } - } - - @Override - public IReadTyped resource(Class theResourceType) { - Validate.notNull(theResourceType, "theResourceType must not be null"); - myType = myContext.getResourceDefinition(theResourceType); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceType)); - } - return this; - } - - @Override - public IReadTyped resource(String theResourceAsText) { - Validate.notBlank(theResourceAsText, "You must supply a value for theResourceAsText"); - myType = myContext.getResourceDefinition(theResourceAsText); - if (myType == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theResourceAsText)); - } - return this; - } - - @Override - public IReadExecutable withId(IIdType theId) { - Validate.notNull(theId, "The ID can not be null"); - Validate.notBlank(theId.getIdPart(), "The ID can not be blank"); - myId = theId.toUnqualified(); - return this; - } - - @Override - public IReadExecutable withId(Long theId) { - Validate.notNull(theId, "The ID can not be null"); - myId = new IdDt(myType.getName(), theId); - return this; - } - - @Override - public IReadExecutable withId(String theId) { - Validate.notBlank(theId, "The ID can not be blank"); - if (theId.indexOf('/') == -1) { - myId = new IdDt(myType.getName(), theId); - } else { - myId = new IdDt(theId); - } - return this; - } - - @Override - public IReadExecutable withIdAndVersion(String theId, String theVersion) { - Validate.notBlank(theId, "The ID can not be blank"); - myId = new IdDt(myType.getName(), theId, theVersion); - return this; - } - - @Override - public IReadExecutable withUrl(IIdType theUrl) { - Validate.notNull(theUrl, "theUrl can not be null"); - myId = theUrl; - processUrl(); - return this; - } - - @Override - public IReadExecutable withUrl(String theUrl) { - myId = new IdDt(theUrl); - processUrl(); - return this; - } - - } - - private final class ResourceListResponseHandler implements IClientResponseHandler> { - - private Class myType; - - public ResourceListResponseHandler(Class theType) { - myType = theType; - } - - @SuppressWarnings("unchecked") - @Override - public List invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) - throws BaseServerResponseException { - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { - Class bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass(); - ResourceResponseHandler handler = new ResourceResponseHandler((Class) bundleType); - IBaseResource response = handler.invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders); - IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory(); - bundleFactory.initializeWithBundleResource(response); - return bundleFactory.toListOfResources(); - } else { - return new ArrayList(new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources()); - } - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private class SearchInternal extends BaseClientExecutable, Object> implements IQuery, IUntypedQuery { - - private String myCompartmentName; - private CriterionList myCriterion = new CriterionList(); - private List myInclude = new ArrayList(); - private DateRangeParam myLastUpdated; - private Integer myParamLimit; - private List> myProfiles = new ArrayList>(); - private String myResourceId; - private String myResourceName; - private Class myResourceType; - private Class myReturnBundleType; - private List myRevInclude = new ArrayList(); - private SearchStyleEnum mySearchStyle; - private String mySearchUrl; - private List mySecurity = new ArrayList(); - private List mySort = new ArrayList(); - private List myTags = new ArrayList(); - - public SearchInternal() { - myResourceType = null; - myResourceName = null; - mySearchUrl = null; - } - - @Override - public IQuery and(ICriterion theCriterion) { - myCriterion.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IQuery byUrl(String theSearchUrl) { - Validate.notBlank(theSearchUrl, "theSearchUrl must not be blank/null"); - - if (theSearchUrl.startsWith("http://") || theSearchUrl.startsWith("https://")) { - mySearchUrl = theSearchUrl; - int qIndex = mySearchUrl.indexOf('?'); - if (qIndex != -1) { - mySearchUrl = mySearchUrl.substring(0, qIndex) + validateAndEscapeConditionalUrl(mySearchUrl.substring(qIndex)); - } - } else { - String searchUrl = theSearchUrl; - if (searchUrl.startsWith("/")) { - searchUrl = searchUrl.substring(1); - } - if (!searchUrl.matches("[a-zA-Z]+($|\\?.*)")) { - 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]"); - } - int qIndex = searchUrl.indexOf('?'); - if (qIndex == -1) { - mySearchUrl = getUrlBase() + '/' + searchUrl; - } else { - mySearchUrl = getUrlBase() + '/' + validateAndEscapeConditionalUrl(searchUrl); - } - } - return this; - } - - @Override - public IQuery count(int theLimitTo) { - if (theLimitTo > 0) { - myParamLimit = theLimitTo; - } else { - myParamLimit = null; - } - return this; - } - - @Override - public IBase execute() { - - Map> params = new LinkedHashMap>(); - // Map> initial = createExtraParams(); - // if (initial != null) { - // params.putAll(initial); - // } - - myCriterion.populateParamList(params); - - for (TokenParam next : myTags) { - addParam(params, Constants.PARAM_TAG, next.getValueAsQueryToken(myContext)); - } - - for (TokenParam next : mySecurity) { - addParam(params, Constants.PARAM_SECURITY, next.getValueAsQueryToken(myContext)); - } - - for (Collection profileUris : myProfiles) { - StringBuilder builder = new StringBuilder(); - for (Iterator profileItr = profileUris.iterator(); profileItr.hasNext();) { - builder.append(profileItr.next()); - if (profileItr.hasNext()) { - builder.append(','); - } - } - addParam(params, Constants.PARAM_PROFILE, builder.toString()); - } - - for (Include next : myInclude) { - if (next.isRecurse()) { - addParam(params, Constants.PARAM_INCLUDE_RECURSE, next.getValue()); - } else { - addParam(params, Constants.PARAM_INCLUDE, next.getValue()); - } - } - - for (Include next : myRevInclude) { - if (next.isRecurse()) { - addParam(params, Constants.PARAM_REVINCLUDE_RECURSE, next.getValue()); - } else { - addParam(params, Constants.PARAM_REVINCLUDE, next.getValue()); - } - } - - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) { - SortSpec rootSs = null; - SortSpec lastSs = null; - for (SortInternal next : mySort) { - SortSpec nextSortSpec = new SortSpec(); - nextSortSpec.setParamName(next.getParamValue()); - nextSortSpec.setOrder(next.getDirection()); - if (rootSs == null) { - rootSs = nextSortSpec; - } else { - lastSs.setChain(nextSortSpec); - } - lastSs = nextSortSpec; - } - if (rootSs != null) { - addParam(params, Constants.PARAM_SORT, SortParameter.createSortStringDstu3(rootSs)); - } - } else { - for (SortInternal next : mySort) { - addParam(params, next.getParamName(), next.getParamValue()); - } - } - - if (myParamLimit != null) { - addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit)); - } - - if (myLastUpdated != null) { - for (DateParam next : myLastUpdated.getValuesAsQueryTokens()) { - addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken(myContext)); - } - } - - if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) { - throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify " - + "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"); - } - - IClientResponseHandler binding; - if (myReturnBundleType != null) { - binding = new ResourceResponseHandler(myReturnBundleType, getPreferResponseTypes(myResourceType)); - } else { - binding = new BundleResponseHandler(myResourceType); - } - - IdDt resourceId = myResourceId != null ? new IdDt(myResourceId) : null; - - BaseHttpClientInvocation invocation; - if (mySearchUrl != null) { - invocation = SearchMethodBinding.createSearchInvocation(myContext, mySearchUrl, params); - } else { - invocation = SearchMethodBinding.createSearchInvocation(myContext, myResourceName, params, resourceId, myCompartmentName, mySearchStyle); - } - - return invoke(params, binding, invocation); - - } - - @Override - public IQuery forAllResources() { - return this; - } - - @Override - public IQuery forResource(Class theResourceType) { - setType(theResourceType); - return this; - } - - @Override - public IQuery forResource(String theResourceName) { - setType(theResourceName); - return this; - } - - @Override - public IQuery include(Include theInclude) { - myInclude.add(theInclude); - return this; - } - - @Override - public IQuery lastUpdated(DateRangeParam theLastUpdated) { - myLastUpdated = theLastUpdated; - return this; - } - - @Override - public IQuery limitTo(int theLimitTo) { - return count(theLimitTo); - } - - @Override - public IQuery returnBundle(Class theClass) { - if (theClass == null) { - throw new NullPointerException("theClass must not be null"); - } - myReturnBundleType = theClass; - return this; - } - - @Override - public IQuery revInclude(Include theInclude) { - myRevInclude.add(theInclude); - return this; - } - - private void setType(Class theResourceType) { - myResourceType = theResourceType; - RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType); - myResourceName = definition.getName(); - } - - private void setType(String theResourceName) { - myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass(); - myResourceName = theResourceName; - } - - @Override - public ISort sort() { - SortInternal retVal = new SortInternal(this); - mySort.add(retVal); - return retVal; - } - - @Override - public IQuery usingStyle(SearchStyleEnum theStyle) { - mySearchStyle = theStyle; - return this; - } - - @Override - public IQuery where(ICriterion theCriterion) { - myCriterion.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IQuery withIdAndCompartment(String theResourceId, String theCompartmentName) { - myResourceId = theResourceId; - myCompartmentName = theCompartmentName; - return this; - } - - @Override - public IQuery withProfile(String theProfileUri) { - Validate.notBlank(theProfileUri, "theProfileUri must not be null or empty"); - myProfiles.add(Collections.singletonList(theProfileUri)); - return this; - } - - @Override - public IQuery withAnyProfile(Collection theProfileUris) { - Validate.notEmpty(theProfileUris, "theProfileUris must not be null or empty"); - myProfiles.add(theProfileUris); - return this; - } - - @Override - public IQuery withSecurity(String theSystem, String theCode) { - Validate.notBlank(theCode, "theCode must not be null or empty"); - mySecurity.add(new TokenParam(theSystem, theCode)); - return this; - } - - @Override - public IQuery withTag(String theSystem, String theCode) { - Validate.notBlank(theCode, "theCode must not be null or empty"); - myTags.add(new TokenParam(theSystem, theCode)); - return this; - } - - } - - @SuppressWarnings("rawtypes") - private static class SortInternal implements ISort { - - private SearchInternal myFor; - private String myParamName; - private String myParamValue; - private SortOrderEnum myDirection; - - public SortInternal(SearchInternal theFor) { - myFor = theFor; - } - - @Override - public IQuery ascending(IParam theParam) { - myParamName = Constants.PARAM_SORT_ASC; - myDirection = SortOrderEnum.ASC; - myParamValue = theParam.getParamName(); - return myFor; - } - - @Override - public IQuery ascending(String theParam) { - myParamName = Constants.PARAM_SORT_ASC; - myDirection = SortOrderEnum.ASC; - myParamValue = theParam; - return myFor; - } - - @Override - public IQuery defaultOrder(IParam theParam) { - myParamName = Constants.PARAM_SORT; - myDirection = null; - myParamValue = theParam.getParamName(); - return myFor; - } - - @Override - public IQuery descending(IParam theParam) { - myParamName = Constants.PARAM_SORT_DESC; - myDirection = SortOrderEnum.DESC; - myParamValue = theParam.getParamName(); - return myFor; - } - - @Override - public IQuery descending(String theParam) { - myParamName = Constants.PARAM_SORT_DESC; - myDirection = SortOrderEnum.DESC; - myParamValue = theParam; - return myFor; - } - - public SortOrderEnum getDirection() { - return myDirection; - } - - public String getParamName() { - return myParamName; - } - - public String getParamValue() { - return myParamValue; - } - - } - - private final class StringResponseHandler implements IClientResponseHandler { - - @Override - public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) - throws IOException, BaseServerResponseException { - return IOUtils.toString(theResponseReader); - } - } - - private final class TagListResponseHandler implements IClientResponseHandler { - - @Override - public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); - if (respType == null) { - throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); - } - IParser parser = respType.newParser(myContext); - return parser.parseTagList(theResponseReader); - } - } - - private final class TransactionExecutable extends BaseClientExecutable, T> implements ITransactionTyped { - - private IBaseBundle myBaseBundle; - private Bundle myBundle; - private String myRawBundle; - private EncodingEnum myRawBundleEncoding; - private List myResources; - - public TransactionExecutable(Bundle theResources) { - myBundle = theResources; - } - - public TransactionExecutable(IBaseBundle theBundle) { - myBaseBundle = theBundle; - } - - public TransactionExecutable(List theResources) { - myResources = theResources; - } - - public TransactionExecutable(String theBundle) { - myRawBundle = theBundle; - myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle); - if (myRawBundleEncoding == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public T execute() { - Map> params = new HashMap>(); - if (myResources != null) { - ResourceListResponseHandler binding = new ResourceListResponseHandler(null); - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myResources, myContext); - return (T) invoke(params, binding, invocation); - } else if (myBaseBundle != null) { - ResourceResponseHandler binding = new ResourceResponseHandler(myBaseBundle.getClass(), getPreferResponseTypes()); - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBaseBundle, myContext); - return (T) invoke(params, binding, invocation); - } else if (myRawBundle != null) { - StringResponseHandler binding = new StringResponseHandler(); - /* - * If the user has explicitly requested a given encoding, we may need to re-encode the raw string - */ - if (getParamEncoding() != null) { - if (MethodUtil.detectEncodingNoDefault(myRawBundle) != getParamEncoding()) { - IBaseResource parsed = parseResourceBody(myRawBundle); - myRawBundle = getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed); - } - } - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myRawBundle, myContext); - return (T) invoke(params, binding, invocation); - } else { - BundleResponseHandler binding = new BundleResponseHandler(null); - BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBundle, myContext); - return (T) invoke(params, binding, invocation); - } - } - - } - - private final class TransactionInternal implements ITransaction { - - @Override - public ITransactionTyped withBundle(Bundle theBundle) { - Validate.notNull(theBundle, "theBundle must not be null"); - return new TransactionExecutable(theBundle); - } - - @Override - public ITransactionTyped withBundle(String theBundle) { - Validate.notBlank(theBundle, "theBundle must not be null"); - return new TransactionExecutable(theBundle); - } - - @Override - public ITransactionTyped withBundle(T theBundle) { - Validate.notNull(theBundle, "theBundle must not be null"); - return new TransactionExecutable(theBundle); - } - - @Override - public ITransactionTyped> withResources(List theResources) { - Validate.notNull(theResources, "theResources must not be null"); - return new TransactionExecutable>(theResources); - } - - } - - private class PatchInternal extends BaseClientExecutable implements IPatch, IPatchWithBody, IPatchExecutable, IPatchWithQuery, IPatchWithQueryTyped { - - private CriterionList myCriterionList; - private IIdType myId; - private PreferReturnEnum myPrefer; - private PatchTypeEnum myPatchType; - private String myPatchBody; - private String mySearchUrl; - private String myResourceType; - - @Override - public IPatchWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IPatchWithQuery conditional(String theResourceType) { - Validate.notBlank(theResourceType, "theResourceType must not be null"); - myResourceType = theResourceType; - myCriterionList = new CriterionList(); - return this; - } - - // TODO: This is not longer used.. Deprecate it or just remove it? - @Override - public IPatchWithBody conditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public MethodOutcome execute() { - - if (myPatchType == null) { - throw new InvalidRequestException("No patch type supplied, cannot invoke server"); - } - if (myPatchBody == null) { - throw new InvalidRequestException("No patch body supplied, cannot invoke server"); - } - - BaseHttpClientInvocation invocation; - if (isNotBlank(mySearchUrl)) { - invocation = MethodUtil.createPatchInvocation(myContext, mySearchUrl, myPatchType, myPatchBody); - } else if (myCriterionList != null) { - invocation = MethodUtil.createPatchInvocation(myContext, myPatchType, myPatchBody, myResourceType, myCriterionList.toParamList()); - } else { - if (myId == null || myId.hasIdPart() == false) { - throw new InvalidRequestException("No ID supplied for resource to patch, can not invoke server"); - } - invocation = MethodUtil.createPatchInvocation(myContext, myId, myPatchType, myPatchBody); - } - - addPreferHeader(myPrefer, invocation); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(null, myPrefer); - - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - - } - - @Override - public IPatchExecutable prefer(PreferReturnEnum theReturn) { - myPrefer = theReturn; - return this; - } - - - @Override - public IPatchWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IPatchExecutable withId(IIdType theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (theId.hasIdPart() == false) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IPatchExecutable withId(String theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (isBlank(theId)) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId); - } - myId = new IdDt(theId); - return this; - } - - @Override - public IPatchWithBody withBody(String thePatchBody) { - Validate.notBlank(thePatchBody, "thePatchBody must not be blank"); - - myPatchBody = thePatchBody; - - EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(thePatchBody); - if (encoding == EncodingEnum.XML) { - myPatchType = PatchTypeEnum.XML_PATCH; - } else if (encoding == EncodingEnum.JSON) { - myPatchType = PatchTypeEnum.JSON_PATCH; - } else { - throw new IllegalArgumentException("Unable to determine encoding of patch"); - } - - return this; - } - - @Override - public IPatchWithQuery conditional(Class theClass) { - Validate.notNull(theClass, "theClass must not be null"); - String resourceType = myContext.getResourceDefinition(theClass).getName(); - return conditional(resourceType); - } - - } - - private class UpdateInternal extends BaseClientExecutable implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped { - - private CriterionList myCriterionList; - private IIdType myId; - private PreferReturnEnum myPrefer; - private IBaseResource myResource; - private String myResourceBody; - private String mySearchUrl; - - @Override - public IUpdateWithQueryTyped and(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IUpdateWithQuery conditional() { - myCriterionList = new CriterionList(); - return this; - } - - @Override - public IUpdateTyped conditionalByUrl(String theSearchUrl) { - mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl); - return this; - } - - @Override - public MethodOutcome execute() { - if (myResource == null) { - myResource = parseResourceBody(myResourceBody); - } - - // If an explicit encoding is chosen, we will re-serialize to ensure the right encoding - if (getParamEncoding() != null) { - myResourceBody = null; - } - - BaseHttpClientInvocation invocation; - if (mySearchUrl != null) { - invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, mySearchUrl); - } else if (myCriterionList != null) { - invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, myCriterionList.toParamList()); - } else { - if (myId == null) { - myId = myResource.getIdElement(); - } - - if (myId == null || myId.hasIdPart() == false) { - throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server"); - } - invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext); - } - - addPreferHeader(myPrefer, invocation); - - RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource); - final String resourceName = def.getName(); - - OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer); - - Map> params = new HashMap>(); - return invoke(params, binding, invocation); - - } - - @Override - public IUpdateExecutable prefer(PreferReturnEnum theReturn) { - myPrefer = theReturn; - return this; - } - - @Override - public IUpdateTyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "Resource can not be null"); - myResource = theResource; - return this; - } - - @Override - public IUpdateTyped resource(String theResourceBody) { - Validate.notBlank(theResourceBody, "Body can not be null or blank"); - myResourceBody = theResourceBody; - return this; - } - - @Override - public IUpdateWithQueryTyped where(ICriterion theCriterion) { - myCriterionList.add((ICriterionInternal) theCriterion); - return this; - } - - @Override - public IUpdateExecutable withId(IIdType theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (theId.hasIdPart() == false) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue()); - } - myId = theId; - return this; - } - - @Override - public IUpdateExecutable withId(String theId) { - if (theId == null) { - throw new NullPointerException("theId can not be null"); - } - if (isBlank(theId)) { - throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId); - } - myId = new IdDt(theId); - return this; - } - - } - - private class ValidateInternal extends BaseClientExecutable implements IValidate, IValidateUntyped { - private IBaseResource myResource; - - @Override - public MethodOutcome execute() { - BaseHttpClientInvocation invocation = ValidateMethodBindingDstu2Plus.createValidateInvocation(myContext, myResource); - ResourceResponseHandler handler = new ResourceResponseHandler(null, null); - IBaseOperationOutcome outcome = invoke(null, handler, invocation); - MethodOutcome retVal = new MethodOutcome(); - retVal.setOperationOutcome(outcome); - return retVal; - } - - @Override - public IValidateUntyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "theResource must not be null"); - myResource = theResource; - return this; - } - - @Override - public IValidateUntyped resource(String theResourceRaw) { - Validate.notBlank(theResourceRaw, "theResourceRaw must not be null or blank"); - myResource = parseResourceBody(theResourceRaw); - - EncodingEnum enc = MethodUtil.detectEncodingNoDefault(theResourceRaw); - if (enc == null) { - throw new IllegalArgumentException(myContext.getLocalizer().getMessage(GenericClient.class, "cantDetermineRequestType")); - } - switch (enc) { - case XML: - encodedXml(); - break; - case JSON: - encodedJson(); - break; - } - return this; - } - - } - }