Skip to content

Commit

Permalink
More work on interceptors
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesagnew committed Aug 17, 2015
1 parent bb1e8b9 commit c2fba2c
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 190 deletions.
Expand Up @@ -258,11 +258,12 @@ protected final Object invokeServerMethod(RestfulServer theServer, RequestDetail
if (operationType != null) {
for (IServerInterceptor next : theServer.getInterceptors()) {
ActionRequestDetails details = new ActionRequestDetails(theRequest);
populateActionRequestDetailsForInterceptor(theRequest, details, theMethodParams);
next.incomingRequestPreHandled(operationType, details);
}
}

// Actuall invoke the method
// Actually invoke the method
try {
Method method = getMethod();
return method.invoke(getProvider(), theMethodParams);
Expand All @@ -278,8 +279,7 @@ protected final Object invokeServerMethod(RestfulServer theServer, RequestDetail
}

/**
* Does this method have a parameter annotated with {@link ConditionalParamBinder}. Note that many operations don't
* actually support this paramter, so this will only return true occasionally.
* Does this method have a parameter annotated with {@link ConditionalParamBinder}. Note that many operations don't actually support this paramter, so this will only return true occasionally.
*/
public boolean isSupportsConditional() {
return mySupportsConditional;
Expand All @@ -294,10 +294,9 @@ public boolean isSupportsConditionalMultiple() {

protected byte[] loadRequestContents(RequestDetails theRequest) throws IOException {
/*
* This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on
* servlet-api in clients since there is no point. So we dynamically load a class that does the servlet processing
* in servers. Down the road it may make sense to just split the method binding classes into server and client
* versions, but this isn't actually a huge deal I don't think.
* This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on servlet-api in clients since there is no point. So we dynamically load a class
* that does the servlet processing in servers. Down the road it may make sense to just split the method binding classes into server and client versions, but this isn't actually a huge deal I
* don't think.
*/
IRequestReader reader = ourRequestReader;
if (reader == null) {
Expand All @@ -322,9 +321,28 @@ protected byte[] loadRequestContents(RequestDetails theRequest) throws IOExcepti

InputStream inputStream = reader.getInputStream(theRequest);
byte[] requestContents = IOUtils.toByteArray(inputStream);

theRequest.setRawRequest(requestContents);

return requestContents;
}

/**
* Subclasses may override this method (but should also call super.{@link #populateActionRequestDetailsForInterceptor(ActionRequestDetails, Object[])) to provide method specifics to the
* interceptors.
*
* @param theRequestDetails
* The server request details
* @param theDetails
* The details object to populate
* @param theMethodParams
* The method params as generated by the specific method binding
*/
protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) {
// TODO Auto-generated method stub

}

protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
BaseServerResponseException ex;
switch (theStatusCode) {
Expand All @@ -347,7 +365,7 @@ protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToT
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
// TODO: handle if something other than OO comes back
BaseOperationOutcome operationOutcome = (BaseOperationOutcome) parser.parseResource(theResponseReader);
ex = new UnprocessableEntityException(operationOutcome);
ex = new UnprocessableEntityException(myContext, operationOutcome);
break;
default:
ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
Expand Down Expand Up @@ -414,14 +432,16 @@ public static BaseMethodBinding<?> bindMethod(Method theMethod, FhirContext theC
if (theProvider instanceof IResourceProvider) {
returnTypeFromRp = ((IResourceProvider) theProvider).getResourceType();
if (!verifyIsValidResourceReturnType(returnTypeFromRp)) {
throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned " + toLogString(returnTypeFromRp) + " - Must return a resource type");
throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned "
+ toLogString(returnTypeFromRp) + " - Must return a resource type");
}
}

Class<?> returnTypeFromMethod = theMethod.getReturnType();
if (getTags != null) {
if (!TagList.class.equals(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from type " + theMethod.getDeclaringClass().getCanonicalName() + " is annotated with @" + GetTags.class.getSimpleName() + " but does not return type " + TagList.class.getName());
throw new ConfigurationException("Method '" + theMethod.getName() + "' from type " + theMethod.getDeclaringClass().getCanonicalName() + " is annotated with @"
+ GetTags.class.getSimpleName() + " but does not return type " + TagList.class.getName());
}
} else if (MethodOutcome.class.equals(returnTypeFromMethod)) {
// returns a method outcome
Expand All @@ -434,13 +454,15 @@ public static BaseMethodBinding<?> bindMethod(Method theMethod, FhirContext theC
} else if (Collection.class.isAssignableFrom(returnTypeFromMethod)) {
returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
if (!verifyIsValidResourceReturnType(returnTypeFromMethod) && !isResourceInterface(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns a collection with generic type " + toLogString(returnTypeFromMethod)
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns a collection with generic type " + toLogString(returnTypeFromMethod)
+ " - Must return a resource type or a collection (List, Set) with a resource type parameter (e.g. List<Patient> or List<IBaseResource> )");
}
} else {
if (!isResourceInterface(returnTypeFromMethod) && !verifyIsValidResourceReturnType(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns " + toLogString(returnTypeFromMethod) + " - Must return a resource type (eg Patient, "
+ Bundle.class.getSimpleName() + ", " + IBundleProvider.class.getSimpleName() + ", etc., see the documentation for more details)");
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns " + toLogString(returnTypeFromMethod) + " - Must return a resource type (eg Patient, " + Bundle.class.getSimpleName() + ", " + IBundleProvider.class.getSimpleName()
+ ", etc., see the documentation for more details)");
}
}

Expand Down Expand Up @@ -470,12 +492,13 @@ public static BaseMethodBinding<?> bindMethod(Method theMethod, FhirContext theC
if (returnTypeFromRp != null) {
if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) {
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
throw new ConfigurationException(
"Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type "
+ returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
}
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type " + returnTypeFromAnnotation.getCanonicalName() + " per method annotation - Must return " + returnTypeFromRp.getCanonicalName()
+ " (or a subclass of it) per IResourceProvider contract");
throw new ConfigurationException(
"Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type " + returnTypeFromAnnotation.getCanonicalName()
+ " per method annotation - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
}
returnType = returnTypeFromAnnotation;
} else {
Expand All @@ -484,8 +507,8 @@ public static BaseMethodBinding<?> bindMethod(Method theMethod, FhirContext theC
} else {
if (!isResourceInterface(returnTypeFromAnnotation)) {
if (!verifyIsValidResourceReturnType(returnTypeFromAnnotation)) {
throw new ConfigurationException(
"Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns " + toLogString(returnTypeFromAnnotation) + " according to annotation - Must return a resource type");
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns " + toLogString(returnTypeFromAnnotation) + " according to annotation - Must return a resource type");
}
returnType = returnTypeFromAnnotation;
} else {
Expand Down Expand Up @@ -600,7 +623,8 @@ public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNext
if (obj1 == null) {
obj1 = object;
} else {
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both.");
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @"
+ obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both.");
}

}
Expand Down
Expand Up @@ -83,6 +83,7 @@ private void addLocationHeader(RequestDetails theRequest, HttpServletResponse th
b.append(response.getVersionId().getValue());
}
theResponse.addHeader(headerLocation, b.toString());

}

protected abstract void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams);
Expand Down Expand Up @@ -140,6 +141,7 @@ public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponse
@Override
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
byte[] requestContents = loadRequestContents(theRequest);

// if (requestContainsResource()) {
// requestContents = parseIncomingServerResource(theRequest);
// } else {
Expand Down
Expand Up @@ -35,11 +35,12 @@
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;

abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding {
private Integer myIdParamIndex;
private String myResourceName;
private int myResourceParameterIndex;
private int myResourceParameterIndex = -1;
private Class<? extends IBaseResource> myResourceType;
private Class<? extends IIdType> myIdParamType;

Expand Down Expand Up @@ -86,6 +87,22 @@ public BaseOutcomeReturningMethodBindingWithResourceParam(Method theMethod, Fhir

}

@Override
protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) {
super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams);

/*
* If the method has no parsed resource parameter, we parse here in order to have something for the interceptor.
*/
if (myResourceParameterIndex != -1) {
theDetails.setResource((IBaseResource) theMethodParams[myResourceParameterIndex]);
} else {
theDetails.setResource(ResourceParameter.parseResourceFromRequest(theRequestDetails, this, myResourceType));
}


}

@Override
protected void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams) {
if (myIdParamIndex != null) {
Expand Down
Expand Up @@ -172,7 +172,7 @@ public Object translateQueryParametersIntoServerArgument(RequestDetails theReque
}

Class<? extends IBaseResource> wantedResourceType = theMethodBinding.getContext().getResourceDefinition("Parameters").getImplementingClass();
IBaseResource requestContents = ResourceParameter.loadResourceFromRequest(theRequest, theRequestContents, theMethodBinding, wantedResourceType);
IBaseResource requestContents = ResourceParameter.loadResourceFromRequest(theRequest, theMethodBinding, wantedResourceType);

RuntimeResourceDefinition def = ctx.getResourceDefinition(requestContents);

Expand Down
Expand Up @@ -43,6 +43,7 @@ public class RequestDetails {
private IdDt myId;
private String myOperation;
private Map<String, String[]> myParameters;
private byte[] myRawRequest;
private String myRequestPath;
private RequestTypeEnum myRequestType;
private String myResourceName;
Expand Down Expand Up @@ -78,6 +79,10 @@ public Map<String, String[]> getParameters() {
return myParameters;
}

public byte[] getRawRequest() {
return myRawRequest;
}

/**
* The part of the request URL that comes after the server base.
* <p>
Expand Down Expand Up @@ -172,6 +177,10 @@ public void setParameters(Map<String, String[]> theParams) {

}

public void setRawRequest(byte[] theRawRequest) {
myRawRequest = theRawRequest;
}

public void setRequestPath(String theRequestPath) {
assert theRequestPath.length() == 0 || theRequestPath.charAt(0) != '/';
myRequestPath = theRequestPath;
Expand Down

0 comments on commit c2fba2c

Please sign in to comment.