From 064f113133c6d2b0c8fd10ed2d9a9ef18ee09d7c Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 31 May 2015 19:38:01 -0400 Subject: [PATCH] Clean up resource parameter handling --- .../rest/client/RestfulClientFactory.java | 3 + .../fhir/rest/method/BaseMethodBinding.java | 215 +++++++++--------- .../BaseOutcomeReturningMethodBinding.java | 99 +------- ...turningMethodBindingWithResourceParam.java | 30 --- .../BaseResourceReturningMethodBinding.java | 24 +- .../rest/method/ConditionalParamBinder.java | 2 +- .../uhn/fhir/rest/method/CountParameter.java | 2 +- .../fhir/rest/method/CreateMethodBinding.java | 15 -- .../fhir/rest/method/DeleteMethodBinding.java | 5 - .../rest/method/DynamicSearchParameter.java | 2 +- .../ca/uhn/fhir/rest/method/IParameter.java | 3 +- .../ca/uhn/fhir/rest/method/MethodUtil.java | 12 +- .../rest/method/NarrativeModeParameter.java | 2 +- .../uhn/fhir/rest/method/NullParameter.java | 2 +- .../rest/method/OperationMethodBinding.java | 22 +- ...ramBinder.java => OperationParameter.java} | 22 +- .../rest/method/ServerBaseParamBinder.java | 2 +- .../rest/method/ServletRequestParameter.java | 2 +- .../rest/method/ServletResponseParameter.java | 2 +- .../uhn/fhir/rest/method/SinceParameter.java | 2 +- .../uhn/fhir/rest/method/SortParameter.java | 2 +- .../rest/method/TransactionMethodBinding.java | 15 +- .../fhir/rest/param/BaseQueryParameter.java | 3 +- .../fhir/rest/param/ResourceParameter.java | 155 ++++++++++++- .../TransactionParameter.java} | 25 +- .../ca/uhn/fhir/i18n/hapi-messages.properties | 7 +- hapi-fhir-jpaserver-base/.gitignore | 1 + .../ClientServerValidationTestDstu.java | 8 +- .../model/primitive/BaseDateTimeDtTest.java | 2 +- .../fhir/validation/InstanceValidator.java | 3 - 30 files changed, 332 insertions(+), 357 deletions(-) rename hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/{OperationParamBinder.java => OperationParameter.java} (90%) rename hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/{method/TransactionParamBinder.java => param/TransactionParameter.java} (90%) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index 6aed21cba12..1dec5c83350 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -85,10 +85,12 @@ public class RestfulClientFactory implements IRestfulClientFactory { myContext = theFhirContext; } + @Override public int getConnectionRequestTimeout() { return myConnectionRequestTimeout; } + @Override public int getConnectTimeout() { return myConnectTimeout; } @@ -274,6 +276,7 @@ public class RestfulClientFactory implements IRestfulClientFactory { myHttpClient = null; } + @SuppressWarnings("unchecked") void validateServerBase(String theServerBase, HttpClient theHttpClient, BaseClient theClient) { GenericClient client = new GenericClient(myContext, theHttpClient, theServerBase, this); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java index 74bf4654e47..bd0883659e4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java @@ -98,6 +98,51 @@ public abstract class BaseMethodBinding implements IClientResponseHandler myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getResourceOperationType()); } + protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) { + EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType); + if (encoding == null) { + NonFhirResponseException ex = NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); + populateException(ex, theResponseReader); + throw ex; + } + + IParser parser = encoding.newParser(getContext()); + return parser; + } + + protected IParser createAppropriateParserForParsingServerRequest(Request theRequest) { + String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type"); + EncodingEnum encoding; + if (isBlank(contentTypeHeader)) { + encoding = EncodingEnum.XML; + } else { + int semicolon = contentTypeHeader.indexOf(';'); + if (semicolon != -1) { + contentTypeHeader = contentTypeHeader.substring(0, semicolon); + } + encoding = EncodingEnum.forContentType(contentTypeHeader); + } + + if (encoding == null) { + throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader); + } + + IParser parser = encoding.newParser(getContext()); + return parser; + } + + protected Object[] createParametersForServerRequest(Request theRequest, byte[] theRequestContents) { + Object[] params = new Object[getParameters().size()]; + for (int i = 0; i < getParameters().size(); i++) { + IParameter param = getParameters().get(i); + if (param == null) { + continue; + } + params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theRequestContents, this); + } + return params; + } + public List> getAllowableParamAnnotations() { return null; } @@ -116,11 +161,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler return retVal; } - public Method getMethod() { + public Method getMethod() { return myMethod; } - public OtherOperationTypeEnum getOtherOperationType() { + public OtherOperationTypeEnum getOtherOperationType() { return null; } @@ -128,7 +173,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler return myParameters; } - @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getProvider() { + return myProvider; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) public Set getRequestIncludesFromParams(Object[] params) { if (params == null || params.length == 0) return null; @@ -163,16 +212,14 @@ public abstract class BaseMethodBinding implements IClientResponseHandler return null; } - public Object getProvider() { - return myProvider; - } - /** * Returns the name of the resource this method handles, or null if this method is not resource * specific */ public abstract String getResourceName(); + public abstract RestfulOperationTypeEnum getResourceOperationType(); + /** * Returns the value of {@link #getResourceOperationType()} or {@link #getSystemOperationType()} or {@link #getOtherOperationType()} */ @@ -192,8 +239,6 @@ public abstract class BaseMethodBinding implements IClientResponseHandler return null; } - public abstract RestfulOperationTypeEnum getResourceOperationType(); - public abstract RestfulOperationSystemEnum getSystemOperationType(); public abstract boolean incomingServerRequestMatchesMethod(Request theRequest); @@ -202,56 +247,6 @@ public abstract class BaseMethodBinding implements IClientResponseHandler public abstract void invokeServer(RestfulServer theServer, Request theRequest) throws BaseServerResponseException, IOException; - /** For unit tests only */ - public void setParameters(List theParameters) { - myParameters = theParameters; - } - - protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) { - EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType); - if (encoding == null) { - NonFhirResponseException ex = NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); - populateException(ex, theResponseReader); - throw ex; - } - - IParser parser = encoding.newParser(getContext()); - return parser; - } - - protected IParser createAppropriateParserForParsingServerRequest(Request theRequest) { - String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type"); - EncodingEnum encoding; - if (isBlank(contentTypeHeader)) { - encoding = EncodingEnum.XML; - } else { - int semicolon = contentTypeHeader.indexOf(';'); - if (semicolon != -1) { - contentTypeHeader = contentTypeHeader.substring(0, semicolon); - } - encoding = EncodingEnum.forContentType(contentTypeHeader); - } - - if (encoding == null) { - throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader); - } - - IParser parser = encoding.newParser(getContext()); - return parser; - } - - protected Object[] createParametersForServerRequest(Request theRequest, IBaseResource theResource) { - Object[] params = new Object[getParameters().size()]; - for (int i = 0; i < getParameters().size(); i++) { - IParameter param = getParameters().get(i); - if (param == null) { - continue; - } - params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theResource); - } - return params; - } - protected Object invokeServerMethod(Object[] theMethodParams) { try { Method method = getMethod(); @@ -267,6 +262,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler } } + protected byte[] loadRequestContents(Request theRequest) throws IOException { + byte[] requestContents = IOUtils.toByteArray(theRequest.getServletRequest().getInputStream()); + return requestContents; + } + protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) { BaseServerResponseException ex; switch (theStatusCode) { @@ -300,6 +300,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler return ex; } + /** For unit tests only */ + public void setParameters(List theParameters) { + myParameters = theParameters; + } + @SuppressWarnings("unchecked") public static BaseMethodBinding bindMethod(Method theMethod, FhirContext theContext, Object theProvider) { Read read = theMethod.getAnnotation(Read.class); @@ -470,6 +475,52 @@ public abstract class BaseMethodBinding implements IClientResponseHandler // return sm; } + private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) { + try { + String responseText = IOUtils.toString(theResponseReader); + theEx.setResponseBody(responseText); + } catch (IOException e) { + ourLog.debug("Failed to read response", e); + } + } + + private static String toLogString(Class theType) { + if (theType == null) { + return null; + } + return theType.getCanonicalName(); + } + + protected static IBundleProvider toResourceList(Object response) throws InternalErrorException { + if (response == null) { + return BundleProviders.newEmptyList(); + } else if (response instanceof IBundleProvider) { + return (IBundleProvider) response; + } else if (response instanceof IResource) { + return BundleProviders.newList((IResource) response); + } else if (response instanceof Collection) { + List retVal = new ArrayList(); + for (Object next : ((Collection) response)) { + retVal.add((IBaseResource) next); + } + return BundleProviders.newList(retVal); + } else { + throw new InternalErrorException("Unexpected return type: " + response.getClass().getCanonicalName()); + } + } + + private static boolean verifyIsValidResourceReturnType(Class theReturnType) { + if (theReturnType == null) { + return false; + } + if (!IBaseResource.class.isAssignableFrom(theReturnType)) { + return false; + } + return true; +// boolean retVal = Modifier.isAbstract(theReturnType.getModifiers()) == false; +// return retVal; + } + public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) { Object obj1 = null; for (Object object : theAnnotations) { @@ -493,50 +544,4 @@ public abstract class BaseMethodBinding implements IClientResponseHandler return true; } - private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) { - try { - String responseText = IOUtils.toString(theResponseReader); - theEx.setResponseBody(responseText); - } catch (IOException e) { - ourLog.debug("Failed to read response", e); - } - } - - private static String toLogString(Class theType) { - if (theType == null) { - return null; - } - return theType.getCanonicalName(); - } - - private static boolean verifyIsValidResourceReturnType(Class theReturnType) { - if (theReturnType == null) { - return false; - } - if (!IBaseResource.class.isAssignableFrom(theReturnType)) { - return false; - } - return true; -// boolean retVal = Modifier.isAbstract(theReturnType.getModifiers()) == false; -// return retVal; - } - - protected static IBundleProvider toResourceList(Object response) throws InternalErrorException { - if (response == null) { - return BundleProviders.newEmptyList(); - } else if (response instanceof IBundleProvider) { - return (IBundleProvider) response; - } else if (response instanceof IResource) { - return BundleProviders.newList((IResource) response); - } else if (response instanceof Collection) { - List retVal = new ArrayList(); - for (Object next : ((Collection) response)) { - retVal.add((IBaseResource) next); - } - return BundleProviders.newList(retVal); - } else { - throw new InternalErrorException("Unexpected return type: " + response.getClass().getCanonicalName()); - } - } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java index 6b91c1c4869..9e5f2b4f65b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java @@ -20,30 +20,21 @@ package ca.uhn.fhir.rest.method; * #L% */ -import static org.apache.commons.lang3.StringUtils.*; - import java.io.IOException; import java.io.Reader; -import java.io.StringReader; import java.io.Writer; import java.lang.reflect.Method; -import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; -import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.parser.IParser; @@ -56,7 +47,6 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding { @@ -149,24 +139,14 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) { - String nextTagComplete = enumeration.nextElement(); - MethodUtil.parseTagValue(tagList, nextTagComplete); - } - if (tagList.isEmpty() == false) { - ((IResource)resource).getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList); - } - } - } else { - resource = null; - } + byte[] requestContents = loadRequestContents(theRequest); +// if (requestContainsResource()) { +// requestContents = parseIncomingServerResource(theRequest); +// } else { +// requestContents = null; +// } - Object[] params = createParametersForServerRequest(theRequest, resource); + Object[] params = createParametersForServerRequest(theRequest, requestContents); addParametersForServerRequest(theRequest, params); HttpServletResponse servletResponse = theRequest.getServletResponse(); @@ -274,73 +254,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding wantedResourceType = requestContainsResourceType(); - IBaseResource retVal; - if (wantedResourceType != null) { - retVal = parser.parseResource(wantedResourceType, requestReader); - } else { - retVal = parser.parseResource(requestReader); - } - - retVal.setId(theRequest.getId()); - - return retVal; - } - protected abstract Set provideAllowableRequestTypes(); - /** - * Subclasses may override if the incoming request should not contain a resource - */ - protected boolean requestContainsResource() { - return true; - } - - /** - * Subclasses may override to provide a specific resource type that this method wants as a parameter - */ - protected Class requestContainsResourceType() { - return null; - } - protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, Request theRequest) throws IOException { theResponse.setStatus(theE.getStatusCode()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java index c55b59c63c1..b2239eb88d4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBindingWithResourceParam.java @@ -20,12 +20,9 @@ package ca.uhn.fhir.rest.method; * #L% */ -import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.ConfigurationException; @@ -34,14 +31,12 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; import ca.uhn.fhir.rest.param.ResourceParameter; -import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding { private int myResourceParameterIndex; private String myResourceName; - private boolean myBinary; private Class myResourceType; private Integer myIdParamIndex; @@ -64,10 +59,6 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu providerResourceType = ((IResourceProvider) theProvider).getResourceType(); } - if (IBaseBinary.class.isAssignableFrom(providerResourceType)) { - myBinary = true; - } - myResourceType = resourceParameter.getResourceType(); if (Modifier.isAbstract(myResourceType.getModifiers())) { myResourceType = providerResourceType; @@ -88,27 +79,6 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu } - @Override - protected Class requestContainsResourceType() { - return myResourceType; - } - - @Override - protected IBaseResource parseIncomingServerResource(Request theRequest) throws IOException { - if (myBinary) { - String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE); - byte[] contents = IOUtils.toByteArray(theRequest.getServletRequest().getInputStream()); - - IBaseBinary binary = (IBaseBinary) getContext().getResourceDefinition("Binary").newInstance(); - binary.setContentType(ct); - binary.setContent(contents); - - return binary; - } else { - return super.parseIncomingServerResource(theRequest); - } - } - @Override protected void addParametersForServerRequest(Request theRequest, Object[] theParams) { if (myIdParamIndex != null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java index 6cfd3664291..b234de7b7a7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.method; * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.*; import java.io.IOException; import java.io.Reader; @@ -37,20 +37,18 @@ import java.util.Set; import javax.servlet.http.HttpServletResponse; -import ca.uhn.fhir.model.api.Include; - import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; -import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IBundleProvider; @@ -244,14 +242,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding 0) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/CreateMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/CreateMethodBinding.java index 56532f5704a..01e81e1683a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/CreateMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/CreateMethodBinding.java @@ -20,13 +20,10 @@ package ca.uhn.fhir.rest.method; * #L% */ -import java.io.IOException; import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; -import org.hl7.fhir.instance.model.api.IBaseResource; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; @@ -55,18 +52,6 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe protected Set provideAllowableRequestTypes() { return Collections.singleton(RequestTypeEnum.POST); } - - @Override - protected IBaseResource parseIncomingServerResource(Request theRequest) throws IOException { - IBaseResource retVal = super.parseIncomingServerResource(theRequest); - - if (theRequest.getId() != null && theRequest.getId().hasIdPart()) { - retVal.setId(theRequest.getId()); - } - - return retVal; - } - @Override protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java index abd11e53f36..51c04c95942 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DeleteMethodBinding.java @@ -81,11 +81,6 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding { } - @Override - protected boolean requestContainsResource() { - return false; - } - @Override protected boolean allowVoidReturnType() { return true; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchParameter.java index 730476574ec..b6bce57264f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DynamicSearchParameter.java @@ -66,7 +66,7 @@ public class DynamicSearchParameter implements IParameter { @SuppressWarnings("unchecked") @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { SearchParameterMap retVal = new SearchParameterMap(); for (String next : theRequest.getParameters().keySet()) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParameter.java index 7c4cbedc36d..c84c3fc3f43 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParameter.java @@ -45,9 +45,10 @@ public interface IParameter { * The incoming request object * @param theRequestContents * The parsed contents of the incoming request. E.g. if the request was an HTTP POST with a resource in the body, this argument would contain the parsed {@link IResource} instance. + * @param theMethodBinding TODO * @return Returns the argument object as it will be passed to the {@link IResourceProvider} method. */ - Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException; + Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException; void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index a2fb14c21d8..474d340a625 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.rest.method; -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.*; import java.io.IOException; import java.io.PushbackReader; @@ -25,10 +24,10 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.DateUtils; +import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IAnyResource; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; @@ -76,6 +75,7 @@ import ca.uhn.fhir.rest.param.ReferenceAndListParam; import ca.uhn.fhir.rest.param.ResourceParameter; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TransactionParameter; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider; @@ -411,7 +411,7 @@ public class MethodUtil { if (!IResource.class.isAssignableFrom(parameterType)) { throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + ResourceParam.class.getSimpleName() + " but has a type that is not an implemtation of " + IResource.class.getCanonicalName()); } - param = new ResourceParameter((Class) parameterType); + param = new ResourceParameter((Class) parameterType, theProvider); } else if (nextAnnotation instanceof IdParam || nextAnnotation instanceof VersionIdParam) { param = new NullParameter(); } else if (nextAnnotation instanceof ServerBase) { @@ -423,12 +423,12 @@ public class MethodUtil { } else if (nextAnnotation instanceof Sort) { param = new SortParameter(); } else if (nextAnnotation instanceof TransactionParam) { - param = new TransactionParamBinder(theContext); + param = new TransactionParameter(theContext); } else if (nextAnnotation instanceof ConditionalUrlParam) { param = new ConditionalParamBinder(theRestfulOperationTypeEnum); } else if (nextAnnotation instanceof OperationParam) { Operation op = theMethod.getAnnotation(Operation.class); - param = new OperationParamBinder(op.name(), (OperationParam) nextAnnotation); + param = new OperationParameter(op.name(), (OperationParam) nextAnnotation); } else { continue; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NarrativeModeParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NarrativeModeParameter.java index 4c388c2f2d2..2a4e24fb08e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NarrativeModeParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NarrativeModeParameter.java @@ -47,7 +47,7 @@ class NarrativeModeParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { String val = theRequest.getServletRequest().getParameter(Constants.PARAM_NARRATIVE); if (val != null) { try { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NullParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NullParameter.java index 3149fd245fe..0f81f69cef8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NullParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/NullParameter.java @@ -39,7 +39,7 @@ class NullParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { // nothing return null; } 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 d60630b511a..4b927183dc0 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 @@ -20,11 +20,8 @@ package ca.uhn.fhir.rest.method; * #L% */ -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.*; -import java.io.BufferedReader; -import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -46,13 +43,10 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; -import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IBundleProvider; -import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; @@ -196,20 +190,6 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { return retVal; } - @Override - protected Object parseRequestObject(Request theRequest) throws IOException { - EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest); - IParser parser = encoding.newParser(getContext()); - BufferedReader requestReader = theRequest.getServletRequest().getReader(); - - if (theRequest.getRequestType() == RequestTypeEnum.GET) { - return null; - } - - Class wantedResourceType = getContext().getResourceDefinition("Parameters").getImplementingClass(); - return parser.parseResource(wantedResourceType, requestReader); - } - public static BaseHttpClientInvocation createOperationInvocation(FhirContext theContext, String theResourceName, String theId, String theOperationName, IBaseParameters theInput, boolean theUseHttpGet) { StringBuilder b = new StringBuilder(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParamBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java similarity index 90% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParamBinder.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java index 1e80c299c08..3242b3ea7d7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParamBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java @@ -44,11 +44,14 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.param.CollectionBinder; +import ca.uhn.fhir.rest.param.ResourceParameter; +import ca.uhn.fhir.rest.server.EncodingEnum; +import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; -class OperationParamBinder implements IParameter { +class OperationParameter implements IParameter { private final String myName; private Class myParameterType; @@ -56,7 +59,7 @@ class OperationParamBinder implements IParameter { private Class myInnerCollectionType; private final String myOperationName; - OperationParamBinder(String theOperationName, OperationParam theAnnotation) { + OperationParameter(String theOperationName, OperationParam theAnnotation) { myOperationName = theOperationName; myName = theAnnotation.name(); } @@ -109,7 +112,7 @@ class OperationParamBinder implements IParameter { @SuppressWarnings("unchecked") @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { List matchingParamValues = new ArrayList(); if (theRequest.getRequestType() == RequestTypeEnum.GET) { @@ -125,16 +128,21 @@ class OperationParamBinder implements IParameter { } } else { HapiLocalizer localizer = theRequest.getServer().getFhirContext().getLocalizer(); - String msg = localizer.getMessage(OperationParamBinder.class, "urlParamNotPrimitive", myOperationName, myName); + String msg = localizer.getMessage(OperationParameter.class, "urlParamNotPrimitive", myOperationName, myName); throw new MethodNotAllowedException(msg, RequestTypeEnum.POST); } } - } else if (theRequestContents == null) { - return null; } else { FhirContext ctx = theRequest.getServer().getFhirContext(); - IBaseResource requestContents = (IBaseResource) theRequestContents; + + if (theRequest.getRequestType() == RequestTypeEnum.GET) { + return null; + } + + Class wantedResourceType = theMethodBinding.getContext().getResourceDefinition("Parameters").getImplementingClass(); + IBaseResource requestContents = ResourceParameter.loadResourceFromRequest(theRequest, theRequestContents, theMethodBinding, wantedResourceType); + RuntimeResourceDefinition def = ctx.getResourceDefinition(requestContents); BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServerBaseParamBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServerBaseParamBinder.java index 69edfecde15..bc471fc6e88 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServerBaseParamBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServerBaseParamBinder.java @@ -43,7 +43,7 @@ class ServerBaseParamBinder implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { return theRequest.getFhirServerBase(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletRequestParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletRequestParameter.java index 9b89e3351a8..263369ec09a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletRequestParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletRequestParameter.java @@ -43,7 +43,7 @@ class ServletRequestParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { return theRequest.getServletRequest(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletResponseParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletResponseParameter.java index ba329d658bd..5e363d34549 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletResponseParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ServletResponseParameter.java @@ -43,7 +43,7 @@ class ServletResponseParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { return theRequest.getServletResponse(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java index 6094d13fd43..64001c95747 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java @@ -54,7 +54,7 @@ class SinceParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_SINCE); if (sinceParams != null) { if (sinceParams.length > 0) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SortParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SortParameter.java index 4aace519dc9..f0c33c02392 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SortParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SortParameter.java @@ -65,7 +65,7 @@ public class SortParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) { if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) { if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java index e1bdf52f665..796005dac92 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java @@ -20,9 +20,8 @@ package ca.uhn.fhir.rest.method; * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.*; -import java.io.IOException; import java.lang.reflect.Method; import java.util.IdentityHashMap; import java.util.List; @@ -44,7 +43,8 @@ import ca.uhn.fhir.rest.annotation.Transaction; import ca.uhn.fhir.rest.annotation.TransactionParam; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; -import ca.uhn.fhir.rest.method.TransactionParamBinder.ParamStyle; +import ca.uhn.fhir.rest.param.TransactionParameter; +import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle; import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -60,13 +60,13 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding myTransactionParamIndex = -1; int index = 0; for (IParameter next : getParameters()) { - if (next instanceof TransactionParamBinder) { + if (next instanceof TransactionParameter) { if (myTransactionParamIndex != -1) { throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " has multiple parameters annotated with the @" + TransactionParam.class + " annotation, exactly one is required for @" + Transaction.class + " methods"); } myTransactionParamIndex = index; - myTransactionParamStyle = ((TransactionParamBinder) next).getParamStyle(); + myTransactionParamStyle = ((TransactionParameter) next).getParamStyle(); } index++; } @@ -182,11 +182,6 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding return retVal; } - @Override - protected Object parseRequestObject(Request theRequest) throws IOException { - return null; // This is parsed in TransactionParamBinder - } - public static BaseHttpClientInvocation createTransactionInvocation(Bundle theBundle, FhirContext theContext) { return new HttpPostClientInvocation(theContext, theBundle); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java index 47060b6fb01..b1cdeac2b63 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseQueryParameter.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.IParameter; import ca.uhn.fhir.rest.method.QualifiedParamList; import ca.uhn.fhir.rest.method.Request; @@ -129,7 +130,7 @@ public abstract class BaseQueryParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { List paramList = new ArrayList(); String name = getName(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java index 64add581d7b..8d582153510 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ResourceParameter.java @@ -20,26 +20,69 @@ package ca.uhn.fhir.rest.param; * #L% */ +import static org.apache.commons.lang3.StringUtils.*; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.charset.Charset; import java.util.Collection; +import java.util.Enumeration; import java.util.List; import java.util.Map; +import org.apache.commons.io.IOUtils; +import org.apache.http.entity.ContentType; +import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.api.TagList; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.IParameter; +import ca.uhn.fhir.rest.method.MethodUtil; import ca.uhn.fhir.rest.method.Request; +import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.EncodingEnum; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class ResourceParameter implements IParameter { - private Class myResourceType; + private boolean myBinary; + private Class myResourceType; - public ResourceParameter(Class theParameterType) { + public ResourceParameter(Class theParameterType, Object theProvider) { myResourceType = theParameterType; + myBinary = IBaseBinary.class.isAssignableFrom(theParameterType); + + Class providerResourceType = null; + if (theProvider instanceof IResourceProvider) { + providerResourceType = ((IResourceProvider) theProvider).getResourceType(); + } + + if (Modifier.isAbstract(myResourceType.getModifiers()) && providerResourceType != null) { + myResourceType = providerResourceType; + } + + } + + public Class getResourceType() { + return myResourceType; + } + + @Override + public void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType) { + // ignore for now } @Override @@ -49,18 +92,110 @@ public class ResourceParameter implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { - IResource resource = (IResource) theRequestContents; - return resource; + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { + + if (myBinary) { + + FhirContext ctx = theRequest.getServer().getFhirContext(); + String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE); + IBaseBinary binary = (IBaseBinary) ctx.getResourceDefinition("Binary").newInstance(); + binary.setContentType(ct); + binary.setContent(theRequestContents); + + return binary; + } + + IBaseResource retVal = loadResourceFromRequest(theRequest, theRequestContents, theMethodBinding, myResourceType); + + return retVal; } - public Class getResourceType() { - return myResourceType; + public static IBaseResource loadResourceFromRequest(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding, Class theResourceType) { + FhirContext ctx = theRequest.getServer().getFhirContext(); + + final Charset charset = determineRequestCharset(theRequest); + Reader requestReader = createRequestReader(theRequestContents, charset); + + EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest); + if (encoding == null) { + String ctValue = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE); + if (ctValue != null) { + if (ctValue.startsWith("application/x-www-form-urlencoded")) { + String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getResourceOrSystemOperationType()); + throw new InvalidRequestException(msg); + } + } + if (isBlank(ctValue)) { + /* + * If the client didn't send a content type, try to guess + */ + String body; + try { + body = IOUtils.toString(requestReader); + } catch (IOException e) { + // This shouldn't happen since we're reading from a byte array.. + throw new InternalErrorException(e); + } + encoding = MethodUtil.detectEncodingNoDefault(body); + if (encoding == null) { + String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", theMethodBinding.getResourceOrSystemOperationType()); + throw new InvalidRequestException(msg); + } else { + requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset); + } + } else { + String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getResourceOrSystemOperationType()); + throw new InvalidRequestException(msg); + } + } + + IParser parser = encoding.newParser(ctx); + + IBaseResource retVal; + if (theResourceType != null) { + retVal = parser.parseResource(theResourceType, requestReader); + } else { + retVal = parser.parseResource(requestReader); + } + + if (theRequest.getId() != null && theRequest.getId().hasIdPart()) { + retVal.setId(theRequest.getId()); + } + + if (theRequest.getServer().getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { + TagList tagList = new TagList(); + for (Enumeration enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) { + String nextTagComplete = enumeration.nextElement(); + MethodUtil.parseTagValue(tagList, nextTagComplete); + } + if (tagList.isEmpty() == false) { + ((IResource)retVal).getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList); + } + } + return retVal; } - @Override - public void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType) { - // ignore for now + static Reader createRequestReader(byte[] theRequestContents, Charset charset) { + Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset); + return requestReader; + } + + static Reader createRequestReader(Request theRequest, byte[] theRequestContents) { + return createRequestReader(theRequestContents, determineRequestCharset(theRequest)); + } + + static Charset determineRequestCharset(Request theRequest) { + String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE); + + Charset charset = null; + if (isNotBlank(ct)) { + ContentType parsedCt = ContentType.parse(ct); + charset = parsedCt.getCharset(); + } + if (charset == null) { + charset = Charset.forName("UTF-8"); + } + return charset; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionParamBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java similarity index 90% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionParamBinder.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java index dc268dee9bb..cf51eab2cb1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionParamBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TransactionParameter.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.rest.method; +package ca.uhn.fhir.rest.param; /* * #%L @@ -20,8 +20,7 @@ package ca.uhn.fhir.rest.method; * #L% */ -import java.io.BufferedReader; -import java.io.IOException; +import java.io.Reader; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -39,18 +38,21 @@ import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.annotation.TransactionParam; +import ca.uhn.fhir.rest.method.BaseMethodBinding; +import ca.uhn.fhir.rest.method.IParameter; +import ca.uhn.fhir.rest.method.Request; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -class TransactionParamBinder implements IParameter { +public class TransactionParameter implements IParameter { private FhirContext myContext; private ParamStyle myParamStyle; private Class myResourceBundleType; - public TransactionParamBinder(FhirContext theContext) { + public TransactionParameter(FhirContext theContext) { myContext = theContext; } @@ -96,20 +98,15 @@ class TransactionParamBinder implements IParameter { } @Override - public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException { + public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { // TODO: don't use a default encoding, just fail! EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest); IParser parser = encoding.newParser(myContext); - BufferedReader reader; - try { - reader = theRequest.getServletRequest().getReader(); - } catch (IOException e) { - throw new InternalErrorException("Failed to read incoming payload", e); - } - + Reader reader = ResourceParameter.createRequestReader(theRequest, theRequestContents); + switch (myParamStyle) { case DSTU1_BUNDLE: { Bundle bundle; @@ -137,7 +134,7 @@ class TransactionParamBinder implements IParameter { return myParamStyle; } - enum ParamStyle { + public enum ParamStyle { /** Old style bundle (defined in hapi-fhir-base) */ DSTU1_BUNDLE, /** New style bundle (defined in hapi-fhir-structures-* as a resource definition itself */ diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index 0b7677b150c..68a894dd8d0 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -13,11 +13,9 @@ ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable ca.uhn.fhir.rest.client.RestfulClientFactory.failedToRetrieveConformance=Failed to retrieve the server's metadata statement during client initialization. URL used was: {0} ca.uhn.fhir.rest.client.RestfulClientFactory.wrongVersionInConformance=The server at base URL "{0}" returned a conformance statement indicating that it supports FHIR version "{1}" which corresponds to {2}, but this client is configured to use {3} (via the FhirContext). -ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.noContentTypeInRequest=No Content-Type header was provided in the request. This is required for "{0}" operation -ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.invalidContentTypeInRequest=Incorrect Content-Type header value of "{0}" was provided in the request. A FHIR Content-Type is required for "{1}" operation ca.uhn.fhir.rest.method.OperationMethodBinding.methodNotSupported=HTTP Method {0} is not allowed for this operation. Allowed method(s): {1} -ca.uhn.fhir.rest.method.OperationParamBinder.urlParamNotPrimitive=Can not invoke operation {0} using HTTP GET because parameter {1} is not a primitive datatype +ca.uhn.fhir.rest.method.OperationParameter.urlParamNotPrimitive=Can not invoke operation {0} using HTTP GET because parameter {1} is not a primitive datatype ca.uhn.fhir.rest.method.IncludeParameter.invalidIncludeNameInRequest=Invalid {2} parameter value: "{0}". Valid values are: {1} ca.uhn.fhir.rest.method.IncludeParameter.orIncludeInRequest='OR' query parameters (values containing ',') are not supported in _include parameters @@ -25,6 +23,9 @@ ca.uhn.fhir.rest.method.SearchMethodBinding.invalidSpecialParamName=Method [{0}] ca.uhn.fhir.rest.method.SearchMethodBinding.idWithoutCompartment=Method [{0}] in provider [{1}] has an @IdParam parameter. This is only allowable for compartment search (e.g. @Search(compartment="foo") ) ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search +ca.uhn.fhir.rest.param.ResourceParameter.invalidContentTypeInRequest=Incorrect Content-Type header value of "{0}" was provided in the request. A FHIR Content-Type is required for "{1}" operation +ca.uhn.fhir.rest.param.ResourceParameter.noContentTypeInRequest=No Content-Type header was provided in the request. This is required for "{0}" operation + ca.uhn.fhir.parser.ParserState.wrongResourceTypeFound=Incorrect resource type found, expected "{0}" but found "{1}" ca.uhn.fhir.rest.server.RestfulServer.getPagesNonHttpGet=Requests for _getpages must use HTTP GET diff --git a/hapi-fhir-jpaserver-base/.gitignore b/hapi-fhir-jpaserver-base/.gitignore index f988cea1dde..b53df3969af 100644 --- a/hapi-fhir-jpaserver-base/.gitignore +++ b/hapi-fhir-jpaserver-base/.gitignore @@ -128,3 +128,4 @@ local.properties .texlipse /target/ +/target/ diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu.java index adbb358da8d..530bdaf6975 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu.java @@ -27,7 +27,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.primitive.UriDt; -import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; +import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException; import ca.uhn.fhir.rest.server.Constants; public class ClientServerValidationTestDstu { @@ -70,7 +70,7 @@ public class ClientServerValidationTestDstu { when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); - myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE); + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); IGenericClient client = myCtx.newRestfulGenericClient("http://foo"); // don't load the conformance until the first time the client is actually used @@ -98,11 +98,11 @@ public class ClientServerValidationTestDstu { when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); - myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE); + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); try { myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/1")); fail(); - } catch (FhirClientConnectionException e) { + } catch (FhirClientInappropriateForServerException e) { assertThat(e.toString(), containsString("The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.4.0\" which corresponds to DSTU2, but this client is configured to use DSTU1 (via the FhirContext)")); } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java index 2fea918e557..100fc199630 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java @@ -78,7 +78,7 @@ public class BaseDateTimeDtTest { */ ourDefaultLocale = Locale.getDefault(); - Locale[] available = Locale.getAvailableLocales(); + Locale[] available = { Locale.CANADA, Locale.GERMANY, Locale.TAIWAN }; Locale newLocale = available[(int) (Math.random() * available.length)]; Locale.setDefault(newLocale); diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/InstanceValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/InstanceValidator.java index 75c947cfe42..97da2926ca2 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/InstanceValidator.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/validation/InstanceValidator.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.validation; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; import java.util.Collections; @@ -15,7 +14,6 @@ import org.hl7.fhir.instance.model.StructureDefinition; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.utils.WorkerContext; import org.hl7.fhir.instance.validation.ValidationMessage; -import org.hl7.fhir.utilities.xml.XMLUtil; import org.w3c.dom.Document; import org.xml.sax.InputSource; @@ -28,7 +26,6 @@ public class InstanceValidator { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InstanceValidator.class); private FhirContext myCtx; - private DocumentBuilderFactory myDocBuilderFactory; InstanceValidator(FhirContext theContext) {