From 330dbde983c71470b9226fc32f9d8d0f6fb40661 Mon Sep 17 00:00:00 2001
From: jamesagnew
Date: Sun, 16 Aug 2015 22:09:01 -0400
Subject: [PATCH] Add new interceptor hook for auditing
---
hapi-fhir-android/pom.xml | 1 -
hapi-fhir-base/pom.xml | 1 -
.../fhir/rest/api/RestOperationTypeEnum.java | 17 +-
.../rest/method/AddTagsMethodBinding.java | 2 +-
.../BaseAddOrDeleteTagsMethodBinding.java | 4 +-
.../fhir/rest/method/BaseMethodBinding.java | 42 ++-
.../BaseOutcomeReturningMethodBinding.java | 4 +-
.../BaseResourceReturningMethodBinding.java | 4 +-
.../rest/method/ConformanceMethodBinding.java | 7 +-
.../fhir/rest/method/CreateMethodBinding.java | 2 +-
.../fhir/rest/method/DeleteMethodBinding.java | 2 +-
.../rest/method/DeleteTagsMethodBinding.java | 2 +-
.../method/DynamicSearchMethodBinding.java | 7 +-
.../rest/method/GetTagsMethodBinding.java | 4 +-
.../rest/method/HistoryMethodBinding.java | 7 +-
.../rest/method/OperationMethodBinding.java | 7 +-
.../fhir/rest/method/ReadMethodBinding.java | 16 +-
.../uhn/fhir/rest/method/RequestDetails.java | 14 +-
.../fhir/rest/method/SearchMethodBinding.java | 7 +-
.../rest/method/TransactionMethodBinding.java | 9 +-
.../fhir/rest/method/UpdateMethodBinding.java | 2 +-
.../method/ValidateMethodBindingDstu1.java | 2 +-
.../fhir/rest/param/ResourceParameter.java | 6 +-
.../uhn/fhir/rest/server/RestfulServer.java | 18 +-
.../interceptor/IServerActionInterceptor.java | 64 -----
.../interceptor/IServerInterceptor.java | 67 ++++-
.../interceptor/InterceptorAdapter.java | 6 +-
.../interceptor/LoggingInterceptor.java | 8 +-
hapi-fhir-jpaserver-base/pom.xml | 13 +-
.../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 28 +-
.../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 82 +++++-
.../fhir/jpa/dao/BaseHapiFhirSystemDao.java | 14 +
.../java/ca/uhn/fhir/jpa/dao/DaoConfig.java | 75 ++++--
.../fhir/jpa/dao/FhirResourceDaoDstu2.java | 19 +-
.../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 21 +-
.../jpa/dao/SearchParamExtractorDstu1.java | 2 +-
.../jpa/dao/SearchParamExtractorDstu2.java | 2 +-
.../provider/JpaResourceProviderDstu2.java | 11 +-
.../jpa/dao/FhirResourceDaoDstu2Test.java | 113 ++++++--
.../fhir-jpabase-spring-test-config.xml | 17 +-
hapi-fhir-jpaserver-example/pom.xml | 21 +-
hapi-fhir-structures-dstu/pom.xml | 1 -
.../interceptor/AuditingInterceptor.java | 6 +-
.../provider/ServerConformanceProvider.java | 6 +-
.../uhn/fhir/rest/server/InterceptorTest.java | 4 +
hapi-fhir-structures-dstu2/pom.xml | 2 -
.../dstu2/ServerConformanceProvider.java | 8 +-
.../ca/uhn/fhir/model/dstu2/BundleTest.java | 3 +-
.../ServerActionInterceptorTest.java | 250 ++++++++++++++++++
hapi-fhir-structures-hl7org-dstu2/pom.xml | 1 -
.../conf/ServerConformanceProvider.java | 8 +-
pom.xml | 44 ++-
.../restful-server-interceptors-exception.xml | 2 +-
.../restful-server-interceptors.xml | 2 +-
.../restful-server-interceptors-exception.svg | 4 +-
.../svg/restful-server-interceptors.svg | 3 +
src/site/xdoc/doc_rest_server_interceptor.xml | 73 ++++-
57 files changed, 893 insertions(+), 274 deletions(-)
delete mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerActionInterceptor.java
create mode 100644 hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java
create mode 100644 src/site/resources/svg/restful-server-interceptors.svg
diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml
index 1237c4eff71..1dd81dce50a 100644
--- a/hapi-fhir-android/pom.xml
+++ b/hapi-fhir-android/pom.xml
@@ -93,7 +93,6 @@
javax.servletjavax.servlet-api
- ${servlet_api_version}compile
diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml
index 41f0b501388..dadb9fac6cb 100644
--- a/hapi-fhir-base/pom.xml
+++ b/hapi-fhir-base/pom.xml
@@ -149,7 +149,6 @@
javax.servletjavax.servlet-api
- ${servlet_api_version}provided
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java
index 5caec93c273..efeef2b5587 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java
@@ -115,7 +115,22 @@ public enum RestOperationTypeEnum {
/**
* Load the server's metadata
*/
- METADATA("metadata"),
+ METADATA("metadata"),
+
+ /**
+ * $meta-add extended operation
+ */
+ META_ADD("$meta-add"),
+
+ /**
+ * $meta-add extended operation
+ */
+ META("$meta"),
+
+ /**
+ * $meta-delete extended operation
+ */
+ META_DELETE("$meta-delete"),
;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/AddTagsMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/AddTagsMethodBinding.java
index 7ba72d8ed42..7f9f7204ba2 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/AddTagsMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/AddTagsMethodBinding.java
@@ -38,7 +38,7 @@ class AddTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.ADD_TAGS;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java
index feb50c6911c..00a79098224 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseAddOrDeleteTagsMethodBinding.java
@@ -107,7 +107,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return null;
}
@@ -171,7 +171,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
} finally {
reader.close();
}
- invokeServerMethod(params);
+ invokeServerMethod(theServer, theRequest, params);
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
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 71743b35c75..063d69fa671 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
@@ -19,7 +19,7 @@ package ca.uhn.fhir.rest.method;
* limitations under the License.
* #L%
*/
-import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.InputStream;
@@ -79,6 +79,8 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.ReflectionUtil;
public abstract class BaseMethodBinding implements IClientResponseHandler {
@@ -102,7 +104,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler
myMethod = theMethod;
myContext = theContext;
myProvider = theProvider;
- myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getResourceOperationType());
+ myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getRestOperationType());
for (IParameter next : myParameters) {
if (next instanceof ConditionalParamBinder) {
@@ -232,7 +234,17 @@ public abstract class BaseMethodBinding implements IClientResponseHandler
*/
public abstract String getResourceName();
- public abstract RestOperationTypeEnum getResourceOperationType();
+ public abstract RestOperationTypeEnum getRestOperationType();
+
+ /**
+ * Determine which operation is being fired for a specific request
+ *
+ * @param theRequestDetails
+ * The request
+ */
+ public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
+ return getRestOperationType();
+ }
public abstract boolean incomingServerRequestMatchesMethod(RequestDetails theRequest);
@@ -240,7 +252,17 @@ public abstract class BaseMethodBinding implements IClientResponseHandler
public abstract void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException;
- protected Object invokeServerMethod(Object[] theMethodParams) {
+ protected final Object invokeServerMethod(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) {
+ // Handle server action interceptors
+ RestOperationTypeEnum operationType = getRestOperationType(theRequest);
+ if (operationType != null) {
+ for (IServerInterceptor next : theServer.getInterceptors()) {
+ ActionRequestDetails details = new ActionRequestDetails(theRequest);
+ next.incomingRequestPreHandled(operationType, details);
+ }
+ }
+
+ // Actuall invoke the method
try {
Method method = getMethod();
return method.invoke(getProvider(), theMethodParams);
@@ -448,12 +470,12 @@ public abstract class BaseMethodBinding implements IClientResponseHandler
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 {
@@ -462,8 +484,8 @@ public abstract class BaseMethodBinding implements IClientResponseHandler
} 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 {
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 d9c56cd8009..fcc9de5a0fc 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
@@ -152,7 +152,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.GET_TAGS;
}
@@ -178,7 +178,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding {
params[myVersionIdParamIndex] = theRequest.getId();
}
- TagList resp = (TagList) invokeServerMethod(params);
+ TagList resp = (TagList) invokeServerMethod(theServer, theRequest, params);
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java
index ab99665155f..f5a2a0330b9 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/HistoryMethodBinding.java
@@ -43,6 +43,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@@ -87,7 +88,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return myResourceOperationType;
}
@@ -154,12 +155,12 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
- public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
+ public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
if (myIdParamIndex != null) {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
- Object response = invokeServerMethod(theMethodParams);
+ Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
final IBundleProvider resources = toResourceList(response);
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 95b29965026..153c1cead64 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
@@ -49,6 +49,7 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.IBundleProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
@@ -166,7 +167,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return myOtherOperatiopnType;
}
@@ -222,7 +223,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
- public Object invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
+ public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
if (theRequest.getRequestType() == RequestTypeEnum.POST) {
// always ok
} else if (theRequest.getRequestType() == RequestTypeEnum.GET) {
@@ -244,7 +245,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
- Object response = invokeServerMethod(theMethodParams);
+ Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
IBundleProvider retVal = toResourceList(response);
return retVal;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java
index a9a21e22d5b..8be6efc006f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java
@@ -48,6 +48,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@@ -91,6 +92,15 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
}
+ @Override
+ public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
+ if (mySupportsVersion && theRequestDetails.getId().hasVersionIdPart()) {
+ return RestOperationTypeEnum.VREAD;
+ } else {
+ return RestOperationTypeEnum.READ;
+ }
+ }
+
@Override
public List> getAllowableParamAnnotations() {
ArrayList> retVal = new ArrayList>();
@@ -99,7 +109,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return isVread() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ;
}
@@ -192,13 +202,13 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
}
@Override
- public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
+ public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
theMethodParams[myIdIndex] = MethodUtil.convertIdToType(theRequest.getId(), myIdParameterType);
if (myVersionIdIndex != null) {
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getId().getVersionIdPart());
}
- Object response = invokeServerMethod(theMethodParams);
+ Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
IBundleProvider retVal = toResourceList(response);
if (theRequest.getServer().getETagSupport() == ETagSupportEnum.ENABLED) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestDetails.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestDetails.java
index 686de0f82b0..634d5ead11e 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestDetails.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RequestDetails.java
@@ -46,8 +46,8 @@ public class RequestDetails {
private String myRequestPath;
private RequestTypeEnum myRequestType;
private String myResourceName;
- private RestOperationTypeEnum myResourceOperationType;
private boolean myRespondGzip;
+ private RestOperationTypeEnum myRestOperationType;
private String mySecondaryOperation;
private RestfulServer myServer;
private HttpServletRequest myServletRequest;
@@ -96,8 +96,8 @@ public class RequestDetails {
return myResourceName;
}
- public RestOperationTypeEnum getResourceOperationType() {
- return myResourceOperationType;
+ public RestOperationTypeEnum getRestOperationType() {
+ return myRestOperationType;
}
public String getSecondaryOperation() {
@@ -185,14 +185,14 @@ public class RequestDetails {
myResourceName = theResourceName;
}
- public void setResourceOperationType(RestOperationTypeEnum theResourceOperationType) {
- myResourceOperationType = theResourceOperationType;
- }
-
public void setRespondGzip(boolean theRespondGzip) {
myRespondGzip = theRespondGzip;
}
+ public void setRestOperationType(RestOperationTypeEnum theRestOperationType) {
+ myRestOperationType = theRestOperationType;
+ }
+
public void setSecondaryOperation(String theSecondaryOperation) {
mySecondaryOperation = theSecondaryOperation;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java
index 36d94ecdce5..d2282611c83 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java
@@ -48,6 +48,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@@ -127,7 +128,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.SEARCH_TYPE;
}
@@ -277,12 +278,12 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
- public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
+ public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
if (myIdParamIndex != null) {
theMethodParams[myIdParamIndex] = theRequest.getId();
}
- Object response = invokeServerMethod(theMethodParams);
+ Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
return toResourceList(response);
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 eb8bdd04167..bab344c93b9 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
@@ -44,6 +44,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
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.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@@ -75,7 +76,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.TRANSACTION;
}
@@ -118,7 +119,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
@SuppressWarnings("unchecked")
@Override
- public Object invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
+ public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
/*
* The design of HAPI's transaction method for DSTU1 support assumed that a transaction was just an update on a
@@ -127,7 +128,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
*/
if (myTransactionParamStyle == ParamStyle.RESOURCE_BUNDLE) {
// This is the DSTU2 style
- Object response = invokeServerMethod(theMethodParams);
+ Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
return response;
}
@@ -145,7 +146,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
}
// Call the server implementation method
- Object response = invokeServerMethod(theMethodParams);
+ Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
IBundleProvider retVal = toResourceList(response);
/*
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java
index 353c4de05b9..05da56b6a61 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java
@@ -47,7 +47,7 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.UPDATE;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java
index ded918aec45..0949c6ea175 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ValidateMethodBindingDstu1.java
@@ -44,7 +44,7 @@ public class ValidateMethodBindingDstu1 extends BaseOutcomeReturningMethodBindin
}
@Override
- public RestOperationTypeEnum getResourceOperationType() {
+ public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.VALIDATE;
}
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 0f10477990b..10e07a8bc16 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
@@ -166,7 +166,7 @@ public class ResourceParameter implements IParameter {
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.getResourceOperationType());
+ String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
throw new InvalidRequestException(msg);
}
}
@@ -183,13 +183,13 @@ public class ResourceParameter implements IParameter {
}
encoding = MethodUtil.detectEncodingNoDefault(body);
if (encoding == null) {
- String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", theMethodBinding.getResourceOperationType());
+ String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", theMethodBinding.getRestOperationType());
throw new InvalidRequestException(msg);
} else {
requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset);
}
} else {
- String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getResourceOperationType());
+ String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
throw new InvalidRequestException(msg);
}
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
index 7f32d9360f2..a5695effa6a 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
@@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.server;
* limitations under the License.
* #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.IOException;
import java.lang.annotation.Annotation;
@@ -640,7 +639,7 @@ public class RestfulServer extends HttpServlet {
String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
if (getPagingProvider() != null && isNotBlank(pagingAction)) {
- requestDetails.setResourceOperationType(RestOperationTypeEnum.GET_PAGE);
+ requestDetails.setRestOperationType(RestOperationTypeEnum.GET_PAGE);
if (theRequestType != RequestTypeEnum.GET) {
/*
* We reconstruct the link-self URL using the request parameters, and this would break if the parameters came in using a POST. We could probably work around that but why bother unless
@@ -665,8 +664,9 @@ public class RestfulServer extends HttpServlet {
}
}
- requestDetails.setResourceOperationType(resourceMethod.getResourceOperationType());
+ requestDetails.setRestOperationType(resourceMethod.getRestOperationType());
+ // Handle server interceptors
for (IServerInterceptor next : myInterceptors) {
boolean continueProcessing = next.incomingRequestPostProcessed(requestDetails, theRequest, theResponse);
if (!continueProcessing) {
@@ -674,7 +674,15 @@ public class RestfulServer extends HttpServlet {
return;
}
}
-
+
+ /*
+ * Actualy invoke the server method. This call is to a HAPI method
+ * binding, which is an object that wraps a specific implementing (user-supplied)
+ * method, but handles its input and provides its output back to the client.
+ *
+ * This is basically the end of processing for a successful request,
+ * since the method binding replies to the client and closes the response.
+ */
resourceMethod.invokeServer(this, requestDetails);
} catch (NotModifiedException e) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerActionInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerActionInterceptor.java
deleted file mode 100644
index 62cfba95db8..00000000000
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerActionInterceptor.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package ca.uhn.fhir.rest.server.interceptor;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.instance.model.api.IIdType;
-
-/**
- * Action interceptors are invoked by the server upon specific fhir operations, such as "read" (HTTP GET) or "create" (HTTP POST). They can be thought of as being a layer "above"
- * {@link IServerInterceptor} interceptors.
- *
- * These interceptors are useful as a means of adding authentication checks or audit operations on top of a server, since the HAPI RestfulServer translates the incoming requests into higher level
- * operations.
- *
- *
- * Note that unlike {@link IServerInterceptor}s, {@link IServerActionInterceptor}s do not have the ability to handle a request themselves and stop processing.
- *
- */
-public interface IServerActionInterceptor extends IServerInterceptor {
-
- /**
- * Invoked before an incoming request is processed
- *
- * @param theServletRequest
- * The incoming servlet request as provided by the servlet container
- * @param theOperation
- * The type of operation that the FHIR server has determined that the client is trying to invoke
- * @param theRequestDetails
- * An object which will be populated with any relevant details about the incoming request
- */
- void preAction(HttpServletRequest theServletRequest, ActionOperationEnum theOperation, ActionRequestDetails theRequestDetails);
-
- /**
- * Represents the type of operation being invoked for a {@link IServerActionInterceptor#preAction(HttpServletRequest, ActionOperationEnum, ActionRequestDetails) preAction} call
- */
- public static enum ActionOperationEnum {
- READ, VREAD
- }
-
- public static class ActionRequestDetails {
- private final IIdType myId;
- private final IBaseResource myRequestResource;
-
- public ActionRequestDetails(IIdType theId, IBaseResource theRequestResource) {
- super();
- myId = theId;
- myRequestResource = theRequestResource;
- }
-
- /**
- * Returns the ID of the incoming request (typically this is from the request URL)
- */
- public IIdType getId() {
- return myId;
- }
-
- /**
- * Returns the incoming resource from the request (this will be populated only for operations which receive a resource, such as "create" and "update")
- */
- public IBaseResource getRequestResource() {
- return myRequestResource;
- }
- }
-}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
index cbfa4cda2fd..b7dc9efb2cd 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
@@ -27,12 +27,14 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
@@ -44,8 +46,6 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
* See: See the server
* interceptor documentation for more information on how to use this class.
*
- *
- * @see
*/
public interface IServerInterceptor {
@@ -83,7 +83,7 @@ public interface IServerInterceptor {
* @throws IOException
* If this exception is thrown, it will be re-thrown up to the container for handling.
*/
- public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException;
+ boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException;
/**
* This method is called just before the actual implementing server method is invoked.
@@ -109,7 +109,7 @@ public interface IServerInterceptor {
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
- public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
+ boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
/**
* This method is called before any other processing takes place for each incoming request. It may be used to provide
@@ -129,7 +129,7 @@ public interface IServerInterceptor {
* must return false. In this case, no further processing will occur and no further interceptors
* will be called.
*/
- public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse);
+ boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
@@ -153,7 +153,7 @@ public interface IServerInterceptor {
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
- public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
+ boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
@@ -177,7 +177,7 @@ public interface IServerInterceptor {
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
- public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
+ boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
@@ -203,7 +203,7 @@ public interface IServerInterceptor {
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
- public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
+ boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
@@ -229,20 +229,33 @@ public interface IServerInterceptor {
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
- public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
+ boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
+
+ /**
+ * Invoked before an incoming request is processed
+ *
+ * @param theServletRequest
+ * The incoming servlet request as provided by the servlet container
+ * @param theOperation
+ * The type of operation that the FHIR server has determined that the client is trying to invoke
+ * @param theRequestDetails
+ * An object which will be populated with any relevant details about the incoming request (this includes
+ * the HttpServletRequest)
+ */
+ void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequestDetails);
/**
* This method is called upon any exception being thrown within the server's request processing code. This includes
* any exceptions thrown within resource provider methods (e.g. {@link Search} and {@link Read} methods) as well as
* any runtime exceptions thrown by the server itself. This method is invoked for each interceptor (until one of them
* returns a non-null response or the end of the list is reached), after which
- * {@link #handleException(RequestDetails, BaseServerResponseException, HttpServletRequest, HttpServletResponse)} is called for each
- * interceptor.
+ * {@link #handleException(RequestDetails, BaseServerResponseException, HttpServletRequest, HttpServletResponse)} is
+ * called for each interceptor.
*
* This may be used to add an OperationOutcome to a response, or to convert between exception types for any reason.
*
*
- * Implementations of this method may choose to ignore/log/count/etc exceptions, and return true. In
+ * Implementations of this method may choose to ignore/log/count/etc exceptions, and return null. In
* this case, processing will continue, and the server will automatically generate an {@link BaseOperationOutcome
* OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they
* should return a non-null, to indicate that they have handled the request and processing should stop.
@@ -255,4 +268,34 @@ public interface IServerInterceptor {
*/
BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException;
+ public static class ActionRequestDetails {
+ private final IIdType myId;
+ private final String myResourceType;
+
+ public ActionRequestDetails(IIdType theId, String theResourceType) {
+ myId = theId;
+ myResourceType = theResourceType;
+ }
+
+ public ActionRequestDetails(RequestDetails theRequestDetails) {
+ myId = theRequestDetails.getId();
+ myResourceType = theRequestDetails.getResourceName();
+ }
+
+ /**
+ * Returns the ID of the incoming request (typically this is from the request URL)
+ */
+ public IIdType getId() {
+ return myId;
+ }
+
+ /**
+ * Returns the resource type this request pertains to, or null if this request is not type specific
+ * (e.g. server-history)
+ */
+ public String getResourceType() {
+ return myResourceType;
+ }
+ }
+
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java
index 2bce28a72de..997d64230b9 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java
@@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.TagList;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
@@ -38,8 +39,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
* Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation
* of all methods, always returning true
*/
-@SuppressWarnings("unused")
-public class InterceptorAdapter implements IServerInterceptor, IServerActionInterceptor {
+public class InterceptorAdapter implements IServerInterceptor {
@Override
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) {
@@ -83,7 +83,7 @@ public class InterceptorAdapter implements IServerInterceptor, IServerActionInte
}
@Override
- public void preAction(HttpServletRequest theServletRequest, ActionOperationEnum theOperation, ActionRequestDetails theRequestDetails) {
+ public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequestDetails) {
// nothing
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java
index 429f634e313..78fbda95ffc 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java
@@ -141,13 +141,13 @@ public class LoggingInterceptor extends InterceptorAdapter {
*/
if ("operationType".equals(theKey)) {
- if (myRequestDetails.getResourceOperationType() != null) {
- return myRequestDetails.getResourceOperationType().getCode();
+ if (myRequestDetails.getRestOperationType() != null) {
+ return myRequestDetails.getRestOperationType().getCode();
}
return "";
} else if ("operationName".equals(theKey)) {
- if (myRequestDetails.getResourceOperationType() != null) {
- switch (myRequestDetails.getResourceOperationType()) {
+ if (myRequestDetails.getRestOperationType() != null) {
+ switch (myRequestDetails.getRestOperationType()) {
case EXTENDED_OPERATION_INSTANCE:
case EXTENDED_OPERATION_SERVER:
case EXTENDED_OPERATION_TYPE:
diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml
index 0739e280c86..bacb74272cd 100644
--- a/hapi-fhir-jpaserver-base/pom.xml
+++ b/hapi-fhir-jpaserver-base/pom.xml
@@ -127,20 +127,23 @@
org.apache.derbyderby
- ${derby_version}test
- commons-dbcp
- commons-dbcp
- 1.4
+ org.apache.commons
+ commons-dbcp2
+ test
+
+
+
+ org.mockito
+ mockito-alltestjavax.servletjavax.servlet-api
- 3.1.0provided
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
index 425449f654b..65b159e2646 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
@@ -60,6 +60,11 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
+import com.google.common.base.Function;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
@@ -95,6 +100,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
@@ -105,13 +111,10 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
-import com.google.common.base.Function;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
-
public abstract class BaseHapiFhirDao implements IDao {
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
@@ -305,6 +308,21 @@ public abstract class BaseHapiFhirDao implements IDao {
}
}
+ protected void notifyInterceptors(RestOperationTypeEnum operationType, ActionRequestDetails requestDetails) {
+ if (requestDetails.getId() != null && requestDetails.getId().hasResourceType() && isNotBlank(requestDetails.getResourceType())) {
+ if (requestDetails.getId().getResourceType().equals(requestDetails.getResourceType()) == false) {
+ throw new InternalErrorException("Inconsistent server state - Resource types don't match: " + requestDetails.getId().getResourceType() + " / " + requestDetails.getResourceType());
+ }
+ }
+ List interceptors = getConfig().getInterceptors();
+ if (interceptors == null) {
+ return;
+ }
+ for (IServerInterceptor next : interceptors) {
+ next.incomingRequestPreHandled(operationType, requestDetails);
+ }
+ }
+
protected DaoConfig getConfig() {
return myConfig;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
index 8118304c3a4..b58f3cd1052 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
@@ -106,6 +106,7 @@ 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.BundleEntrySearchModeEnum;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
@@ -125,6 +126,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ObjectUtil;
@@ -1192,6 +1194,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
throw new InvalidRequestException("Trying to update " + theId + " but this is not the current version");
}
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType());
+ notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
+
ResourceTable savedEntity = updateEntity(null, entity, true, new Date());
notifyWriteCompleted();
@@ -1212,9 +1218,14 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
}
Long pid = resource.iterator().next();
-
ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
+ // Notify interceptors
+ IdDt idToDelete = entity.getIdDt();
+ ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType());
+ notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
+
+ // Perform delete
ResourceTable savedEntity = updateEntity(null, entity, true, new Date());
notifyWriteCompleted();
@@ -1241,7 +1252,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
return toMethodOutcome(entity, theResource).setCreated(false);
}
}
-
+
if (isNotBlank(theResource.getId().getIdPart())) {
if (isValidPid(theResource.getId())) {
throw new UnprocessableEntityException("This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
@@ -1259,6 +1270,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
}
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getId(), toResourceName(theResource));
+ notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
+
updateEntity(theResource, entity, false, null, thePerformIndexing, true);
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
@@ -1274,6 +1289,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public TagList getAllResourceTags() {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null,null);
+ notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
+
StopWatch w = new StopWatch();
TagList tags = super.getTags(myResourceType, null);
ourLog.info("Processed getTags on {} in {}ms", myResourceName, w.getMillisAndRestart());
@@ -1286,8 +1305,17 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
return myResourceType;
}
+
+ public String getResourceName() {
+ return myResourceName;
+ }
+
@Override
public TagList getTags(IIdType theResourceId) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, null);
+ notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
+
StopWatch w = new StopWatch();
TagList retVal = super.getTags(myResourceType, theResourceId);
ourLog.info("Processed getTags on {} in {}ms", theResourceId, w.getMillisAndRestart());
@@ -1296,6 +1324,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public IBundleProvider history(Date theSince) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
+ notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
+
StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(myResourceName, null, theSince);
ourLog.info("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
@@ -1304,6 +1336,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public IBundleProvider history(final IIdType theId, final Date theSince) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.HISTORY_INSTANCE, requestDetails);
+
final InstantDt end = createHistoryToTimestamp();
final String resourceType = getContext().getResourceDefinition(myResourceType).getName();
@@ -1400,6 +1436,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public IBundleProvider history(Long theId, Date theSince) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails);
+
StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(myResourceName, theId, theSince);
ourLog.info("Processed history on {} in {}ms", theId, w.getMillisAndRestart());
@@ -1492,6 +1532,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public MetaDt metaAddOperation(IIdType theResourceId, MetaDt theMetaAdd) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.META_ADD, requestDetails);
+
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
@@ -1532,6 +1576,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public MetaDt metaDeleteOperation(IIdType theResourceId, MetaDt theMetaDel) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails);
+
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
@@ -1566,6 +1614,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public MetaDt metaGetOperation() {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
+
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)";
TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class);
q.setParameter("res_type", myResourceName);
@@ -1578,6 +1630,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public MetaDt metaGetOperation(IIdType theId) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
+
Long pid = super.translateForcedIdToPid(theId);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type AND t.myResourceId = :res_id)";
@@ -1622,6 +1678,11 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
public T read(IIdType theId) {
validateResourceTypeAndThrowIllegalArgumentException(theId);
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
+ RestOperationTypeEnum operationType = theId.hasVersionIdPart() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ;
+ notifyInterceptors(operationType, requestDetails);
+
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theId);
validateResourceType(entity);
@@ -1691,6 +1752,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
+
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theId);
if (entity == null) {
@@ -1728,6 +1793,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
@Override
public IBundleProvider search(final SearchParameterMap theParams) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
+
StopWatch w = new StopWatch();
final InstantDt now = InstantDt.withCurrentTime();
@@ -2205,6 +2274,15 @@ public abstract class BaseHapiFhirResourceDao extends BaseH
throw new InvalidRequestException("Trying to update " + resourceId + " but this is not the current version");
}
+ if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) {
+ throw new UnprocessableEntityException("Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]");
+ }
+
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(resourceId, getResourceName());
+ notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails);
+
+ // Perform update
ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true);
notifyWriteCompleted();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
index 386bb146395..3108737b0eb 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
@@ -41,8 +41,10 @@ import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao implements IFhirSystemDao {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
@@ -50,6 +52,10 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao implement
@Transactional(propagation=Propagation.REQUIRED)
@Override
public void deleteAllTagsOnServer() {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
+ notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
+
myEntityManager.createQuery("DELETE from ResourceTag t").executeUpdate();
}
@@ -77,6 +83,10 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao implement
@Override
public IBundleProvider history(Date theSince) {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
+ notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
+
StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(null, null, theSince);
ourLog.info("Processed global history in {}ms", w.getMillisAndRestart());
@@ -85,6 +95,10 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao implement
@Override
public TagList getAllTags() {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
+ notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
+
StopWatch w = new StopWatch();
TagList retVal = super.getTags(null, null);
ourLog.info("Processed getAllTags in {}ms", w.getMillisAndRestart());
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
index 6779fb230b4..77569cd9b9e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
@@ -1,5 +1,9 @@
package ca.uhn.fhir.jpa.dao;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
/*
* #%L
* HAPI FHIR JPA Server
@@ -21,23 +25,16 @@ package ca.uhn.fhir.jpa.dao;
*/
import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
public class DaoConfig {
private int myHardSearchLimit = 1000;
private int myHardTagListLimit = 1000;
- private ResourceEncodingEnum myResourceEncoding=ResourceEncodingEnum.JSONC;
private int myIncludeLimit = 2000;
-
- /**
- * This is the maximum number of resources that will be added to a single page of
- * returned resources. Because of includes with wildcards and other possibilities it is possible for a client to make
- * requests that include very large amounts of data, so this hard limit can be imposed to prevent runaway
- * requests.
- */
- public void setIncludeLimit(int theIncludeLimit) {
- myIncludeLimit = theIncludeLimit;
- }
+ private List myInterceptors;
+ private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
/**
* See {@link #setIncludeLimit(int)}
@@ -50,6 +47,23 @@ public class DaoConfig {
return myHardTagListLimit;
}
+ public int getIncludeLimit() {
+ return myIncludeLimit;
+ }
+
+ /**
+ * Returns the interceptors which will be notified of operations.
+ *
+ * @see #setInterceptors(List)
+ */
+ public List getInterceptors() {
+ return myInterceptors;
+ }
+
+ public ResourceEncodingEnum getResourceEncoding() {
+ return myResourceEncoding;
+ }
+
public void setHardSearchLimit(int theHardSearchLimit) {
myHardSearchLimit = theHardSearchLimit;
}
@@ -58,16 +72,47 @@ public class DaoConfig {
myHardTagListLimit = theHardTagListLimit;
}
- public ResourceEncodingEnum getResourceEncoding() {
- return myResourceEncoding;
+ /**
+ * This is the maximum number of resources that will be added to a single page of returned resources. Because of
+ * includes with wildcards and other possibilities it is possible for a client to make requests that include very
+ * large amounts of data, so this hard limit can be imposed to prevent runaway requests.
+ */
+ public void setIncludeLimit(int theIncludeLimit) {
+ myIncludeLimit = theIncludeLimit;
+ }
+
+ /**
+ * This may be used to optionally register server interceptors directly against the DAOs.
+ *
+ * Registering server action interceptors against the JPA DAOs can be more powerful than registering them against the
+ * {@link RestfulServer}, since the DAOs are able to break transactions into individual actions, and will account for
+ * match URLs (e.g. if a request contains an If-None-Match URL, the ID will be adjusted to account for the matching
+ * ID).
+ *
+ */
+ public void setInterceptors(List theInterceptors) {
+ myInterceptors = theInterceptors;
}
public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
myResourceEncoding = theResourceEncoding;
}
- public int getIncludeLimit() {
- return myIncludeLimit;
+ /**
+ * This may be used to optionally register server interceptors directly against the DAOs.
+ *
+ * Registering server action interceptors against the JPA DAOs can be more powerful than registering them against the
+ * {@link RestfulServer}, since the DAOs are able to break transactions into individual actions, and will account for
+ * match URLs (e.g. if a request contains an If-None-Match URL, the ID will be adjusted to account for the matching
+ * ID).
+ *
+ */
+ public void setInterceptors(IServerInterceptor... theInterceptor) {
+ if (theInterceptor == null || theInterceptor.length==0){
+ setInterceptors(new ArrayList());
+ } else {
+ setInterceptors(Arrays.asList(theInterceptor));
+ }
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java
index 4ce7366de5c..90187cf9ab4 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java
@@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao;
+import java.util.ArrayList;
+
/*
* #%L
* HAPI FHIR JPA Server
@@ -22,7 +24,6 @@ package ca.uhn.fhir.jpa.dao;
import java.util.Collections;
import java.util.List;
-import java.util.ArrayList;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -41,8 +42,10 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.IParserErrorHandler;
import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
@@ -70,12 +73,15 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
OperationOutcome oo = new OperationOutcome();
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
- oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);
+ oo.getIssueFirstRep().getDiagnosticsElement().setValue(theMessage);
return oo;
}
@Override
public MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null);
+ notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
+
final OperationOutcome oo = new OperationOutcome();
IParser parser = theEncoding.newParser(getContext());
@@ -83,17 +89,17 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou
@Override
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
- oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown attribute found: " + theAttributeName);
+ oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDiagnostics("Unknown attribute found: " + theAttributeName);
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
- oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown element found: " + theElementName);
+ oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDiagnostics("Unknown element found: " + theElementName);
}
@Override
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
- oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Multiple repetitions of non-repeatable element found: " + theElementName);
+ oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDiagnostics("Multiple repetitions of non-repeatable element found: " + theElementName);
}
});
@@ -108,11 +114,12 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou
// This method returns a MethodOutcome object
MethodOutcome retVal = new MethodOutcome();
- oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Validation succeeded");
+ oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Validation succeeded");
retVal.setOperationOutcome(oo);
return retVal;
}
+
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java
index 0d2ad1c0d6b..3ae9c313f6d 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java
@@ -19,9 +19,7 @@ package ca.uhn.fhir.jpa.dao;
* limitations under the License.
* #L%
*/
-import static org.apache.commons.lang3.StringUtils.defaultString;
-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.util.Date;
import java.util.HashMap;
@@ -58,12 +56,14 @@ import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
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.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
@@ -137,7 +137,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
Entry nextEntry = resp.addEntry();
OperationOutcome oo = new OperationOutcome();
- oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setDetails(caughtEx.getMessage());
+ oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setDiagnostics(caughtEx.getMessage());
nextEntry.setResource(oo);
EntryResponse nextEntryResp = nextEntry.getResponse();
@@ -148,13 +148,16 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
long delay = System.currentTimeMillis() - start;
ourLog.info("Batch completed in {}ms", new Object[] { delay });
- ooResp.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Batch completed in " + delay + "ms");
+ ooResp.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Batch completed in " + delay + "ms");
return resp;
}
@Override
public MetaDt metaGetOperation() {
+ // Notify interceptors
+ ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
+ notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)";
TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class);
@@ -251,7 +254,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
OperationOutcome statusOperationOutcome = new OperationOutcome();
if (transactionType == null) {
String message = "Transactiion Bundle did not specify valid Bundle.type, assuming " + BundleTypeEnum.TRANSACTION.getCode();
- statusOperationOutcome.addIssue().setCode(IssueTypeEnum.INVALID_CONTENT).setSeverity(IssueSeverityEnum.WARNING).setDetails(message);
+ statusOperationOutcome.addIssue().setCode(IssueTypeEnum.INVALID_CONTENT).setSeverity(IssueSeverityEnum.WARNING).setDiagnostics(message);
ourLog.warn(message);
transactionType = BundleTypeEnum.TRANSACTION;
}
@@ -405,7 +408,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
int configuredMax = 100; // this should probably be configurable or something
if (bundle.size() > configuredMax) {
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.WARNING)
- .setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
+ .setDiagnostics("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
}
List resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax));
for (IBaseResource next : resourcesToAdd) {
@@ -472,7 +475,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
long delay = System.currentTimeMillis() - start;
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
- statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails(theActionName + " completed in " + delay + "ms");
+ statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics(theActionName + " completed in " + delay + "ms");
for (IdDt next : allIds) {
IdDt replacement = idSubstitutions.get(next);
@@ -482,7 +485,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
if (replacement.equals(next)) {
continue;
}
- statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Placeholder resource ID \"" + next + "\" was replaced with permanent ID \"" + replacement + "\"");
+ statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Placeholder resource ID \"" + next + "\" was replaced with permanent ID \"" + replacement + "\"");
}
notifyWriteCompleted();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu1.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu1.java
index d5b14b68bd9..103ed4239c4 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu1.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu1.java
@@ -65,7 +65,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
-class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implements ISearchParamExtractor {
+public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implements ISearchParamExtractor {
public SearchParamExtractorDstu1(FhirContext theContext) {
super(theContext);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java
index 128a9941749..49863dea8b6 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchParamExtractorDstu2.java
@@ -70,7 +70,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
-class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
+public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
public SearchParamExtractorDstu2(FhirContext theContext) {
super(theContext);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
index 4d2b3e78229..4f52d6826de 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaResourceProviderDstu2.java
@@ -52,6 +52,9 @@ import ca.uhn.fhir.validation.ValidationResult;
public class JpaResourceProviderDstu2 extends BaseJpaResourceProvider {
+ public static final String OPERATION_NAME_META = "$meta";
+ public static final String OPERATION_NAME_META_DELETE = "$meta-delete";
+ public static final String OPERATION_NAME_META_ADD = "$meta-add";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaResourceProviderDstu2.class);
public JpaResourceProviderDstu2() {
@@ -91,7 +94,7 @@ public class JpaResourceProviderDstu2 extends BaseJpaResour
}
//@formatter:off
- @Operation(name="$meta", idempotent=true, returnParameters= {
+ @Operation(name=OPERATION_NAME_META, idempotent=true, returnParameters= {
@OperationParam(name="return", type=MetaDt.class)
})
//@formatter:on
@@ -103,7 +106,7 @@ public class JpaResourceProviderDstu2 extends BaseJpaResour
}
//@formatter:off
- @Operation(name="$meta", idempotent=true, returnParameters= {
+ @Operation(name=OPERATION_NAME_META, idempotent=true, returnParameters= {
@OperationParam(name="return", type=MetaDt.class)
})
//@formatter:on
@@ -115,7 +118,7 @@ public class JpaResourceProviderDstu2 extends BaseJpaResour
}
//@formatter:off
- @Operation(name="$meta-add", idempotent=true, returnParameters= {
+ @Operation(name=OPERATION_NAME_META_ADD, idempotent=true, returnParameters= {
@OperationParam(name="return", type=MetaDt.class)
})
//@formatter:on
@@ -127,7 +130,7 @@ public class JpaResourceProviderDstu2 extends BaseJpaResour
}
//@formatter:off
- @Operation(name="$meta-delete", idempotent=true, returnParameters= {
+ @Operation(name=OPERATION_NAME_META_DELETE, idempotent=true, returnParameters= {
@OperationParam(name="return", type=MetaDt.class)
})
//@formatter:on
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
index 881f65746b4..f31f1e1ad0d 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java
@@ -1,7 +1,10 @@
package ca.uhn.fhir.jpa.dao;
+import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -14,12 +17,15 @@ import java.util.Set;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
+import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
@@ -64,6 +70,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.CompositeParam;
@@ -82,6 +89,8 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
@SuppressWarnings("unchecked")
public class FhirResourceDaoDstu2Test extends BaseJpaTest {
@@ -101,22 +110,22 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
private static IFhirResourceDao ourQuestionnaireDao;
private static IFhirSystemDao ourSystemDao;
private static IFhirResourceDao ourPractitionerDao;
+ private static IServerInterceptor ourInterceptor;
private List extractNames(IBundleProvider theSearch) {
ArrayList retVal = new ArrayList();
for (IBaseResource next : theSearch.getResources(0, theSearch.size())) {
- Patient nextPt = (Patient)next;
+ Patient nextPt = (Patient) next;
retVal.add(nextPt.getNameFirstRep().getNameAsSingleString());
}
return retVal;
}
-
+
@Test
public void testCreateOperationOutcome() {
/*
- * If any of this ever fails, it means that one of the OperationOutcome
- * issue severity codes has changed code value across versions. We store
- * the string as a constant, so something will need to be fixed.
+ * If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code
+ * value across versions. We store the string as a constant, so something will need to be fixed.
*/
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
@@ -127,7 +136,46 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
-}
+ }
+
+ @Test
+ public void testRead() {
+ Observation o1 = new Observation();
+ o1.getCode().addCoding().setSystem("foo").setCode("testRead");
+ IIdType id1 = ourObservationDao.create(o1).getId();
+
+ /*
+ * READ
+ */
+
+ reset(ourInterceptor);
+ Observation obs = ourObservationDao.read(id1.toUnqualifiedVersionless());
+ assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
+
+ // Verify interceptor
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture());
+ ActionRequestDetails details = detailsCapt.getValue();
+ assertEquals(id1.toUnqualifiedVersionless().getValue(), details.getId().toUnqualifiedVersionless().getValue());
+ assertEquals("Observation", details.getResourceType());
+
+ /*
+ * VREAD
+ */
+ assertTrue(id1.hasVersionIdPart()); // just to make sure..
+ reset(ourInterceptor);
+ obs = ourObservationDao.read(id1);
+ assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
+
+ // Verify interceptor
+ detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture());
+ details = detailsCapt.getValue();
+ assertEquals(id1.toUnqualified().getValue(), details.getId().toUnqualified().getValue());
+ assertEquals("Observation", details.getResourceType());
+
+ }
+
@Test
public void testChoiceParamConcept() {
@@ -271,6 +319,15 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
IIdType id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
+ // Verify interceptor
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
+ ActionRequestDetails details = detailsCapt.getValue();
+ assertNotNull(details.getId());
+ assertEquals("Patient", details.getResourceType());
+
+ reset(ourInterceptor);
+
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
@@ -278,6 +335,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertEquals(id.getIdPart(), results.getId().getIdPart());
assertFalse(results.getCreated().booleanValue());
+ verifyNoMoreInteractions(ourInterceptor);
+
// Now create a second one
p = new Patient();
@@ -522,7 +581,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().addFamily("Tester_testDeleteThenUndelete").addGiven("Joe");
IIdType id = ourPatientDao.create(patient).getId();
- assertThat(id.getValue(), endsWith("/_history/1"));
+ assertThat(id.getValue(), Matchers.endsWith("/_history/1"));
// should be ok
ourPatientDao.read(id.toUnqualifiedVersionless());
@@ -1476,16 +1535,16 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
patient.addTelecom().setSystem(ContactPointSystemEnum.EMAIL).setValue("abc");
id2 = ourPractitionerDao.create(patient).getId().toUnqualifiedVersionless();
}
-
+
Map params;
List patients;
-
+
params = new HashMap();
params.put(Practitioner.SP_FAMILY, new StringDt(methodName));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(2, patients.size());
assertThat(patients, containsInAnyOrder(id1, id2));
-
+
params = new HashMap();
params.put(Practitioner.SP_FAMILY, new StringParam(methodName));
params.put(Practitioner.SP_EMAIL, new TokenParam(null, "abc"));
@@ -1507,8 +1566,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertThat(patients, containsInAnyOrder(id1));
}
-
-
+
@Test
public void testSearchNameParam() {
IIdType id1;
@@ -1765,7 +1823,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
/*
- * TODO: it's kind of weird that we throw a 404 for textual IDs that don't exist, but just return an empty list for numeric IDs that don't exist
+ * TODO: it's kind of weird that we throw a 404 for textual IDs that don't exist, but just return an empty list
+ * for numeric IDs that don't exist
*/
result = toList(ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam("999999999999999")));
@@ -1773,7 +1832,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
-
@Test
public void testSearchStringParam() {
{
@@ -1800,7 +1858,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
}
-
@Test
public void testSearchStringParamReallyLong() {
String methodName = "testSearchStringParamReallyLong";
@@ -1863,8 +1920,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam001");
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam1");
- patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem")
- .setDisplay("testSearchTokenParamDisplay");
+ patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem").setDisplay("testSearchTokenParamDisplay");
ourPatientDao.create(patient);
patient = new Patient();
@@ -1957,7 +2013,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
@Test
public void testSearchWithTagParameter() {
String methodName = "testSearchWithTagParameter";
-
+
IIdType tag1id;
{
Organization org = new Organization();
@@ -2528,7 +2584,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertThat(actual.subList(0, 2), containsInAnyOrder(id2, id4));
assertThat(actual.subList(2, 4), containsInAnyOrder(id1, id3));
}
-
+
@Test
public void testSortByString01() {
Patient p = new Patient();
@@ -2599,7 +2655,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
p.addIdentifier().setSystem("urn:system").setValue(string);
p.addName().addFamily("Fam2").addGiven("Giv2");
ourPatientDao.create(p).getId().toUnqualifiedVersionless();
-
+
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(string);
p.addName().addFamily("Fam1").addGiven("Giv2");
@@ -2607,7 +2663,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
SearchParameterMap pm;
List names;
-
+
pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
pm.setSort(new SortSpec(Patient.SP_FAMILY));
@@ -2639,7 +2695,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
ourLog.info("Names: {}", names);
assertThat(names.subList(0, 2), contains("Giv2 Fam2", "Giv1 Fam2"));
assertThat(names.subList(2, 4), contains("Giv2 Fam1", "Giv1 Fam1"));
-}
+ }
@Test
public void testStoreUnversionedResources() {
@@ -3011,7 +3067,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
ourOrganizationDao.update(p2);
fail();
} catch (UnprocessableEntityException e) {
- // good
+ ourLog.error("Good", e);
}
}
@@ -3063,8 +3119,19 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
ourQuestionnaireDao = ourCtx.getBean("myQuestionnaireDaoDstu2", IFhirResourceDao.class);
ourQuestionnaireResponseDao = ourCtx.getBean("myQuestionnaireResponseDaoDstu2", IFhirResourceDao.class);
ourFhirCtx = ourCtx.getBean(FhirContext.class);
+
+ ourInterceptor = mock(IServerInterceptor.class);
+
+ DaoConfig daoConfig = ourCtx.getBean(DaoConfig.class);
+ daoConfig.setInterceptors(ourInterceptor);
+
}
+ @Before
+ public void before() {
+ reset(ourInterceptor);
+ }
+
private static void deleteEverything() {
FhirSystemDaoDstu2Test.doDeleteEverything(ourSystemDao);
}
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml b/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml
index 2a8f7ae311b..cc840460f79 100644
--- a/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml
+++ b/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml
@@ -16,19 +16,14 @@
-
+
+
+
+
+
-
-
-
-
-
-
@@ -47,4 +42,4 @@
-
\ No newline at end of file
+
diff --git a/hapi-fhir-jpaserver-example/pom.xml b/hapi-fhir-jpaserver-example/pom.xml
index 73a3a5fe044..7ead7c6697e 100644
--- a/hapi-fhir-jpaserver-example/pom.xml
+++ b/hapi-fhir-jpaserver-example/pom.xml
@@ -18,7 +18,6 @@
ca.uhn.hapi.examplehapi-fhir-jpaserver-example
- 1.2-SNAPSHOTwarHAPI FHIR JPA Server - Example
@@ -82,14 +81,13 @@
ch.qos.logbacklogback-classic
- 1.1.2
+ ${logback_version}javax.servletjavax.servlet-api
- 3.0.1provided
@@ -100,7 +98,7 @@
org.thymeleafthymeleaf
- 2.1.4.RELEASE
+ ${thymeleaf-version}
@@ -134,7 +132,6 @@
org.apache.commonscommons-dbcp2
- 2.0.1
+
+ org.jboss.arquillian.junit
+ arquillian-junit-container
+ test
+
diff --git a/hapi-fhir-structures-dstu/pom.xml b/hapi-fhir-structures-dstu/pom.xml
index e43652d4ee8..f02fdc42752 100644
--- a/hapi-fhir-structures-dstu/pom.xml
+++ b/hapi-fhir-structures-dstu/pom.xml
@@ -24,7 +24,6 @@
javax.servletjavax.servlet-api
- ${servlet_api_version}provided
diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java
index cdcbf8364c7..9df0c1880d1 100644
--- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java
+++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java
@@ -138,7 +138,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
participants.add(participant);
auditEvent.setParticipant(participants);
- SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
+ SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getRestOperationType());
byte[] query = getQueryFromRequestDetails(theRequestDetails);
List auditableObjects = new ArrayList();
for (BundleEntry entry : theResponseObject.getEntries()) {
@@ -193,7 +193,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
auditEvent.setParticipant(participants);
byte[] query = getQueryFromRequestDetails(theRequestDetails);
- SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
+ SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getRestOperationType());
ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle, query);
if (auditableObject == null) {
log.debug("No auditable resources to audit");
@@ -235,7 +235,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
*/
protected Event getEventInfo(RequestDetails theRequestDetails) {
Event event = new Event();
- event.setAction(mapResourceTypeToSecurityEventAction(theRequestDetails.getResourceOperationType()));
+ event.setAction(mapResourceTypeToSecurityEventAction(theRequestDetails.getRestOperationType()));
event.setDateTimeWithMillisPrecision(new Date());
event.setOutcome(SecurityEventOutcomeEnum.SUCCESS); // we audit successful return of PHI only, otherwise an
// exception is thrown and no resources are returned to be
diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
index dd2f1e2fc04..62627facd99 100644
--- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
+++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java
@@ -145,8 +145,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider nameToSearchParam = new HashMap();
for (BaseMethodBinding> nextMethodBinding : next.getMethodBindings()) {
- if (nextMethodBinding.getResourceOperationType() != null) {
- RestfulOperationTypeEnum resOp = RestfulOperationTypeEnum.VALUESET_BINDER.fromCodeString(nextMethodBinding.getResourceOperationType().getCode());
+ if (nextMethodBinding.getRestOperationType() != null) {
+ RestfulOperationTypeEnum resOp = RestfulOperationTypeEnum.VALUESET_BINDER.fromCodeString(nextMethodBinding.getRestOperationType().getCode());
if (resOp != null) {
if (resourceOps.contains(resOp) == false) {
resourceOps.add(resOp);
@@ -154,7 +154,7 @@ public class ServerConformanceProvider implements IServerConformanceProviderjavax.servletjavax.servlet-api
- ${servlet_api_version}provided
@@ -116,7 +115,6 @@
org.mockitomockito-all
- 1.9.5test
diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java
index 484df30642e..d1d5ed313ff 100644
--- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java
+++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java
@@ -101,8 +101,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider systemOps, BaseMethodBinding> nextMethodBinding) {
- if (nextMethodBinding.getResourceOperationType() != null) {
- String sysOpCode = nextMethodBinding.getResourceOperationType().getCode();
+ if (nextMethodBinding.getRestOperationType() != null) {
+ String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
if (sysOpCode != null) {
SystemRestfulInteractionEnum sysOp = SystemRestfulInteractionEnum.VALUESET_BINDER.fromCodeString(sysOpCode);
if (sysOp == null) {
@@ -193,8 +193,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider nameToSearchParam = new HashMap();
for (BaseMethodBinding> nextMethodBinding : nextEntry.getValue()) {
- if (nextMethodBinding.getResourceOperationType() != null) {
- String resOpCode = nextMethodBinding.getResourceOperationType().getCode();
+ if (nextMethodBinding.getRestOperationType() != null) {
+ String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) {
TypeRestfulInteractionEnum resOp = TypeRestfulInteractionEnum.VALUESET_BINDER.fromCodeString(resOpCode);
if (resOp != null) {
diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/dstu2/BundleTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/dstu2/BundleTest.java
index e8aef9a0bcd..b0e1fe1e3fe 100644
--- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/dstu2/BundleTest.java
+++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/dstu2/BundleTest.java
@@ -10,8 +10,7 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle.Link;
public class BundleTest {
@Test
- public void testGetLink() {
-
+ public void testGetLink() {
Bundle b = new Bundle();
Link link = b.getLink(Bundle.LINK_NEXT);
diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java
new file mode 100644
index 00000000000..240c5857c04
--- /dev/null
+++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java
@@ -0,0 +1,250 @@
+package ca.uhn.fhir.rest.server.interceptor;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.dstu2.resource.Patient;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.annotation.Create;
+import ca.uhn.fhir.rest.annotation.History;
+import ca.uhn.fhir.rest.annotation.IdParam;
+import ca.uhn.fhir.rest.annotation.Read;
+import ca.uhn.fhir.rest.annotation.ResourceParam;
+import ca.uhn.fhir.rest.annotation.Update;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
+import ca.uhn.fhir.rest.client.IGenericClient;
+import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
+import ca.uhn.fhir.rest.method.RequestDetails;
+import ca.uhn.fhir.rest.server.BundleInclusionRule;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
+import ca.uhn.fhir.util.PortUtil;
+
+public class ServerActionInterceptorTest {
+
+ private static CloseableHttpClient ourClient;
+ private static final FhirContext ourCtx = FhirContext.forDstu2();
+ private static int ourPort;
+ private static Server ourServer;
+ private static IServerInterceptor ourInterceptor;
+ private static IGenericClient ourFhirClient;
+
+ @Test
+ public void testRead() throws Exception {
+ HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123");
+ CloseableHttpResponse status = ourClient.execute(httpGet);
+ IOUtils.closeQuietly(status.getEntity().getContent());
+
+ assertEquals(200, status.getStatusLine().getStatusCode());
+
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture());
+
+ ActionRequestDetails details = detailsCapt.getValue();
+ assertEquals("Patient/123", details.getId().getValue());
+ }
+
+ @Test
+ public void testVRead() throws Exception {
+ HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456");
+ CloseableHttpResponse status = ourClient.execute(httpGet);
+ IOUtils.closeQuietly(status.getEntity().getContent());
+
+ assertEquals(200, status.getStatusLine().getStatusCode());
+
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture());
+
+ ActionRequestDetails details = detailsCapt.getValue();
+ assertEquals("Patient/123/_history/456", details.getId().getValue());
+ }
+
+ @Test
+ public void testCreate() throws Exception {
+ Patient patient = new Patient();
+ patient.addName().addFamily("FAMILY");
+ ourFhirClient.create().resource(patient).execute();
+
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
+
+ ActionRequestDetails details = detailsCapt.getValue();
+ assertEquals("Patient", details.getResourceType());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ Patient patient = new Patient();
+ patient.addName().addFamily("FAMILY");
+ patient.setId("Patient/123");
+ ourFhirClient.update().resource(patient).execute();
+
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture());
+
+ ActionRequestDetails details = detailsCapt.getValue();
+ assertEquals("Patient", details.getResourceType());
+ assertEquals("Patient/123", details.getId().getValue());
+ }
+
+ @Test
+ public void testHistorySystem() throws Exception {
+ HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history");
+ CloseableHttpResponse status = ourClient.execute(httpGet);
+ IOUtils.closeQuietly(status.getEntity().getContent());
+
+ assertEquals(200, status.getStatusLine().getStatusCode());
+
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_SYSTEM), detailsCapt.capture());
+ }
+
+ @Test
+ public void testHistoryType() throws Exception {
+ HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history");
+ CloseableHttpResponse status = ourClient.execute(httpGet);
+ IOUtils.closeQuietly(status.getEntity().getContent());
+
+ assertEquals(200, status.getStatusLine().getStatusCode());
+
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_TYPE), detailsCapt.capture());
+ assertEquals("Patient", detailsCapt.getValue().getResourceType());
+ }
+
+ @Test
+ public void testHistoryInstance() throws Exception {
+ HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history");
+ CloseableHttpResponse status = ourClient.execute(httpGet);
+ IOUtils.closeQuietly(status.getEntity().getContent());
+
+ assertEquals(200, status.getStatusLine().getStatusCode());
+
+ ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
+ verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_INSTANCE), detailsCapt.capture());
+ assertEquals("Patient", detailsCapt.getValue().getResourceType());
+ assertEquals("Patient/123", detailsCapt.getValue().getId().getValue());
+ }
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ ourPort = PortUtil.findFreePort();
+ ourServer = new Server(ourPort);
+
+ ServletHandler proxyHandler = new ServletHandler();
+ RestfulServer servlet = new RestfulServer(ourCtx);
+ servlet.registerInterceptor(new ResponseHighlighterInterceptor());
+ servlet.setResourceProviders(new DummyPatientResourceProvider());
+ servlet.setPlainProviders(new PlainProvider());
+ servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE);
+ ServletHolder servletHolder = new ServletHolder(servlet);
+ proxyHandler.addServletWithMapping(servletHolder, "/*");
+ ourServer.setHandler(proxyHandler);
+ ourServer.start();
+
+ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
+ HttpClientBuilder builder = HttpClientBuilder.create();
+ builder.setConnectionManager(connectionManager);
+ ourClient = builder.build();
+
+ ourInterceptor = mock(InterceptorAdapter.class);
+ servlet.registerInterceptor(ourInterceptor);
+
+ ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
+ ourFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
+
+
+ }
+
+ @Before
+ public void before() {
+ reset(ourInterceptor);
+
+ when(ourInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
+ when(ourInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
+ when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
+ when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
+ }
+
+ public static class PlainProvider {
+
+ @History()
+ public List history() {
+ Patient retVal = new Patient();
+ retVal.setId("Patient/123/_history/2");
+ return Collections.singletonList((IBaseResource) retVal);
+ }
+
+ }
+
+ public static class DummyPatientResourceProvider implements IResourceProvider {
+
+ @Override
+ public Class extends IBaseResource> getResourceType() {
+ return Patient.class;
+ }
+
+ @Read(version = true)
+ public Patient read(@IdParam IdDt theId) {
+ Patient retVal = new Patient();
+ retVal.setId(theId);
+ return retVal;
+ }
+
+ @History()
+ public List history() {
+ Patient retVal = new Patient();
+ retVal.setId("Patient/123/_history/2");
+ return Collections.singletonList(retVal);
+ }
+
+ @History()
+ public List history(@IdParam IdDt theId) {
+ Patient retVal = new Patient();
+ retVal.setId("Patient/123/_history/2");
+ return Collections.singletonList(retVal);
+ }
+
+ @Create()
+ public MethodOutcome create(@ResourceParam Patient thePatient) {
+ Patient retVal = new Patient();
+ retVal.setId("Patient/123/_history/2");
+ return new MethodOutcome(retVal.getId());
+ }
+
+ @Update()
+ public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
+ Patient retVal = new Patient();
+ retVal.setId("Patient/123/_history/2");
+ return new MethodOutcome(retVal.getId());
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml
index 4446bd8b43f..1ab6f8a043c 100644
--- a/hapi-fhir-structures-hl7org-dstu2/pom.xml
+++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml
@@ -24,7 +24,6 @@
javax.servletjavax.servlet-api
- ${servlet_api_version}provided
diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java
index 9276d642a9b..56cd2909936 100644
--- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java
+++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/conf/ServerConformanceProvider.java
@@ -102,8 +102,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider systemOps,
BaseMethodBinding> nextMethodBinding) {
- if (nextMethodBinding.getResourceOperationType() != null) {
- String sysOpCode = nextMethodBinding.getResourceOperationType().getCode();
+ if (nextMethodBinding.getRestOperationType() != null) {
+ String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
if (sysOpCode != null) {
SystemRestfulInteraction sysOp;
try {
@@ -200,8 +200,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider nameToSearchParam = new HashMap();
for (BaseMethodBinding> nextMethodBinding : nextEntry.getValue()) {
- if (nextMethodBinding.getResourceOperationType() != null) {
- String resOpCode = nextMethodBinding.getResourceOperationType().getCode();
+ if (nextMethodBinding.getRestOperationType() != null) {
+ String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) {
TypeRestfulInteraction resOp;
try {
diff --git a/pom.xml b/pom.xml
index 7e4d98a050d..8eb0ebc8d12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -171,7 +171,7 @@
${user.home}/sites/hapi-fhir${user.home}/sites/scm/hapi-fhir
-
+
4.44.42.4
@@ -209,7 +209,6 @@
2.7.14.3.6UTF-8
- 3.1.01.7.104.1.5.RELEASE3.2.4.RELEASE
@@ -220,6 +219,47 @@
UTF-8
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.1.0
+
+
+ org.apache.commons
+ commons-dbcp2
+ 2.1.1
+
+
+ org.apache.derby
+ derby
+ ${derby_version}
+
+
+ org.apache.derby
+ derbynet
+ ${derby_version}
+
+
+ org.apache.derby
+ derbyclient
+ ${derby_version}
+
+
+ org.jboss.arquillian.junit
+ arquillian-junit-container
+ 1.1.8.Final
+
+
+ org.mockito
+ mockito-all
+ 1.10.19
+
+
+
+
diff --git a/src/site/imgsources/restful-server-interceptors-exception.xml b/src/site/imgsources/restful-server-interceptors-exception.xml
index 4bf83fa7222..d322c998c99 100644
--- a/src/site/imgsources/restful-server-interceptors-exception.xml
+++ b/src/site/imgsources/restful-server-interceptors-exception.xml
@@ -1 +1 @@
-
\ No newline at end of file
+3VlNb9s4EP01PsaQTH/pWCd2d4EWCOoFdvfIWJRFlBa1FJU4/fUdWkN9UbEVR2m79cEwh+SIfPPmcUSPyO3h+FHRNP4sQyZGEy88jsjdaDKZz2fwbQzPhWE69QvDXvGwMNUMW/6NodFDa85DljUGaimF5mnTuJNJwna6YYukaD4ipXvrvjJsd1S41r95qOPCupzMK/sfjO9j+xh/HhQ9mX62PkIW0Vzom5MJ+kz3gVpfp12RNQCmpAQ35tfheMuEAc0CUmx980JvuUjFElzI+Qm4iEcqclzjl/X2ryiHMbAxcqQjstoy9ciUs5kspqn5mR/EJx4xwRNorVKm+IFpGE/uBJrvK9sqK6I4nZvfWsmv7FYKaUbD4zz4bGCp2GNxBlRWEReiNjIINvBxd4sAwHo1Q5qdTLj7j0zCQtQzDLG9MyQTchDx8p6qGPse2uJafMkCjRS5ti9dV3DDD0S8G33ioP9nAjDtWKpho78n4JPllYDPBwB86tKdZTIHxGHmvaA8qfH+XslHEBgIRGn6zHQsw980MlML+8+IzMKJjIMyTABlN8havDNNld5qqo2xBYsBEFbbAW0UYc8AmFkVQMgst2uQkS7EBgBs2aEdO3ngyR6sX9h/Ocu0C2EIZxo2mXiQT+vKsDoZoCOWin+Tiabw6JWBgsMp+EHwfQKdD1JreTCjk/CDUqcJMmXQZSx4Ri/Pwg5oq+d/wOiNZ7b576lJbLOWJneIVUeYMHPr/DEbfEPcugKCPu4lh8dXMtZKlgBrGesBmLlnGie1oloup1egS1LWRQvD6/HMUIsmoWCuLCmZJyHYyZ0PwD7FXLNtSk+IPUFBNgD9SdBEoTxN65phdWToDOgoXs4SPpEnWabIZMEiWMwZNs6JUdsOPoLxyHVtDrQaU6Ddj8EX6XpJe0ujYoJq/tgsVq+nsz/1xv4sqD6YX9ZjkXoOvR2/01Z5dcnvgGnjnvVDEN6S2Z6RAXqtBaqL71af3sR3lJjajorMXx9NxcglFC/X6L2V+J5q/yDk7uuZHJgujPy7OdDKvJdiUTCgGcO+qg794wWiVAVobIG7Ph2MF1zLhQy4hqn4ClmLq2I6VxBOTytjwkrlPQIb0iwuj4hLZ3qvk/iXCRrx8Q38HcTlqnLVgHl1sdorea4uXIndwA+oXDtKV3gLS2UCYJ3juVTw8rWXCRUNsrvKVHLa4GYa91SDChn0ABRveqYIKLKgLAGssnXpXDAzjrp0Dpa0gfiiu+YLXGWpBXmzOQW5lyS+stCFVXveJAiWE8/3FgFpJtqNj1cQP6vOIGQx9ur1QCvr+9YZ8/Y1jr1bvJD7jqMbJGcJEOaF9SOjKINYvFE+UJheJx87IXNw0FHIk1WsD5ZvQ1yKtfS4jH6NJhbwodXB+qhho2OT38D5969zhjkO+yYnmTlHn78gQ9Qrtv1a+rfrdcfRcGUPcd9rX+BpB8b9C3TLpx9QoMPLX3tHqWJwjbhjWfb/LdJbbG6eRzaK/SPUVe8RbwjSgxe4Da0+KOXvQF334r6s2JPcTLuyYu939fYqkaoYAAhXFKhOCie89WL+VwrvbKi7AmhW/7AVw6v/J8n6Ow==
\ No newline at end of file
diff --git a/src/site/imgsources/restful-server-interceptors.xml b/src/site/imgsources/restful-server-interceptors.xml
index 0d00915fd5f..530869baeba 100644
--- a/src/site/imgsources/restful-server-interceptors.xml
+++ b/src/site/imgsources/restful-server-interceptors.xml
@@ -1 +1 @@
-
\ No newline at end of file
+3Vpdb+I6E/41vdwq4JDC5dKW7SudldBS6Zxz6RIHrDUxr2P6sb9+x2RMPuzQFALLKReIjJ2J/cwzj8cOV+R29fpN0fXyu4yZuOoH8esVubvq96NoAN/G8JYbwrCXGxaKx7mpZJjxXwyNAVo3PGZZpaOWUmi+rhrnMk3ZXFdsiRTVR6zpwrovDLM5Fa71bx7rZW4d9qPC/sD4Ymkf04tGeUum36yPmCV0I/SXrQnaTPOKWl/bWZF7AExJCW7Mr9XrLRMGNAtIPvVJQ+tukIqlOJD9N+AgnqnY4Bh/3M8ekw30gYmRV3pFxjOmnplyJpMt6dr83KzEXzxhgqdwNV4zxVdMQ39yJ9A8LWzjLI9iGJnfWsmf7FYKaXrD4wL4TGCo2GJxBlTGCRei1HM0msDHnS0CAOPVDGm2NeHsvzEJA1Fv0MW2DpBMyEHEK3gpYtwL0LYsxTccopEi1xY71wXc8AMR96NPHPT/lwJMc7bWMNHPCXjfAvcnAA9durNMbgBxuHMqKE9LvJ8q+QwCA4HYmb4zvZTxJ41MaGH/aGRGHUTmxomMgzLcAMpukLV4Z5oqPdNUG2MNFgMgjNYDbZJgSweY3VQhsxwtQUY8iFnbMYANHcAeHh+nYPnB/r9hmXbhi2E9w0smnuTLfWEYbw3QsJSK/5KppvDYsYGBwwr4VfBFCo1PUmu5Mr3T+KtS2xvkmkGTseD6PNwLOSCt3v4BY3A9sJf/bi+JvSylyB3i5AkRZm2ZO2aCR8TMFwz0MZUcHl9IWC1RRljHWA/AygXTeFMtorvhtAqyrXSctDiKtMRmNo6/Zx9Toq3tUqatBfoY2vbsaIsZ8XQuVzxdIHGnioHyzlmWMVdpW5HY8rYlhZ+EnP80pleuS9SEq5yZ4Y3htLmuUpOMKXoVLMF888QlZ0M1nm2pCu3XN8iuIljX/RrhPk5Z4wVWjuKDcmI95unVCYXdElMxvVGw0gZaGRPq8CmiHNNsCSwid70WqtVKay4ngqSWjR2qjq9MwkUl4JnZUQmaZTzhngxVcpPGO8xfllyz2Zpu4XuBXWAXJXtYQyLyFCo+/epi2e1h4E6ryL7a62SKjDvYPYosjSp/VknGgB6R0BCaDhLaeDmTJLvF9n9aki8mguHJJHknKY2SvKRpLP6IHhO7/0MYiC2wz6HHnhOsvdxN5XZvXpGJfduSiJgtt2dj0ixOeIsjTk1Iv8vb9zbgO6Nigmr+XD2xPJzPJCLXvUFJkWr0blAkx29YO2N7z2+HaeOesB3CjoZAD2Hp9K9CzXzCe3x8KinjxVOlHtIwGF4HN6WQHsaUOgPfcdshUdyS9wRlXUhwjTlDWWeXs9KM5EYvACso67K1TOEk7ZOVcjaIRxQCEKEOCgHjBcfSffVm3zh9kurtcoIWYcqfQF0OOt02YB58tt0qeQ4+5w4jXFjPcNC976S7hY5JBa9sFjKlosJ5V6B21LZr8ZRqECMDImAThHuqxjwZdmu8FTif3I0GxpG/ZognEGZ0V33tU1hKsZ5MtrFulWQesfzgqTmULhUKfMHLc1Ucg3rFYRfOj9YYUb0arTtqyPvCke0okyQDOI/UBqxp9mkDKEK85arhQUPqejjQui4h9sTvDHUJafMC4KFhM3vKAqWW3B7Ma6nyEcRh2vVVi/SiDlYt8HJYGrgcqDnqbvmD/XBDxXIxpUrH0XZrFEixLqJt/yHVWquaol131F19St59ewBA+g6SW51UHaV79TcHsBac7KQKLou/ceUwFn+CI/e/AQ==
\ No newline at end of file
diff --git a/src/site/resources/svg/restful-server-interceptors-exception.svg b/src/site/resources/svg/restful-server-interceptors-exception.svg
index 86fb174d2c2..b6e67f3c7df 100644
--- a/src/site/resources/svg/restful-server-interceptors-exception.svg
+++ b/src/site/resources/svg/restful-server-interceptors-exception.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/src/site/resources/svg/restful-server-interceptors.svg b/src/site/resources/svg/restful-server-interceptors.svg
new file mode 100644
index 00000000000..0f874ea368a
--- /dev/null
+++ b/src/site/resources/svg/restful-server-interceptors.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/site/xdoc/doc_rest_server_interceptor.xml b/src/site/xdoc/doc_rest_server_interceptor.xml
index c77fb521ebc..7091e36ea1b 100644
--- a/src/site/xdoc/doc_rest_server_interceptor.xml
+++ b/src/site/xdoc/doc_rest_server_interceptor.xml
@@ -10,22 +10,28 @@
-
-
-
+
The RESTful server provides a powerful mechanism for adding cross-cutting behaviour
+ (e.g. requests, such as authnorization, auditing, fancy output, logging, etc.)
to each incoming request that it processes. This mechanism consists of defining one or
more interceptors that will be invoked at defined points in the processing of
each incoming request.
-
+
+
+
Interceptors will intercept the incoming request, and can take action such as
logging or auditing it, or examining/injecting headers. They can optionally choose
- to handle the request themself and the cancel any subsequent processing. Interceptors
+ to handle the request directly and the cancel any subsequent processing (in other words,
+ the interceptor can choose to supply a response to the client, and can then signal
+ to the server that it does not need to do so).
+
+
+ Interceptors
may also be notified of responses prior to those responses being served to a client,
- and may audit or even cancel response. The diagram on the right shows the
+ and may audit or even cancel the response. The diagram on the right shows the
lifecycle of a normal (non failing) request which is subject to an interceptor.
@@ -34,7 +40,7 @@
IServerInterceptor
interface (or extend the convenience
InterceptorAdapter
- class provided). The RESTful server will normally invoke the interceptor at three
+ class provided). The RESTful server will normally invoke the interceptor at several
points in the execution of the client request.
@@ -43,17 +49,62 @@
Before any processing at all is performed on the request,
incomingRequestPreProcessed will be invoked. This can be useful
if you wish to handle some requests completely outside of HAPI's processing
- mechanism. If you are handling a request in your interceptor, you may
- return false from your implementation method to signal to
- HAPI that processing of the request should stop immediately.
+ mechanism.
+
+
+ If this method returns true, processing continues to the
+ next interceptor, and ultimately to the next phase of processing.
+
+
+ If this method returns false, processing stops immediately.
+ This is useful if the interceptor wishes to supply its own response
+ by directly calling methods on the HttpServletResponse
+
+
+ If this method throws any subclass of
+ BaseServerResponseException,
+ processing is stopped immedicately and the corresponding status is returned to the client.
+ This is useful if an interceptor wishes to abort the request (e.g. because
+ it did not detect valid credentials)
+
+
- Once the request is parsed (but before it is handled),
+ Once the request is classified (meaning that the URL and request headers are
+ examined to determine exactly what kind of request is being made),
incomingRequestPostProcessed will be invoked. This method has
an additional parameter, the
RequestDetails
object which contains details about what operation is about to be
called, and what request parameters were receievd with that request.
+
+
+ If this method returns true, processing continues to the
+ next interceptor, and ultimately to the next phase of processing.
+
+
+ If this method returns false, processing stops immediately.
+ This is useful if the interceptor wishes to supply its own response
+ by directly calling methods on the HttpServletResponse
+
+
+ If this method throws any subclass of
+ BaseServerResponseException,
+ processing is stopped immedicately and the corresponding status is returned to the client.
+ This is useful if an interceptor wishes to abort the request (e.g. because
+ it did not detect valid credentials)
+
+
+
+
+ Once the request is being handled,
+ incomingRequestPreHandled will be invoked. This method is useful in that
+ it provides details about the FHIR operation being invoked (e.g. is this a "read" or a "create"? what
+ is the resource type and ID of the resource being accessed, etc.). This method can be
+ useful for adding finer grained access controls. Note that incomingRequestPreHandled
+ is not able to directly supply a response, but it may throw a
+ BaseServerResponseException
+ to abort processing.
After the operation is handled (by invoking the corresponding ResourceProvider or PlainProvider method),