diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index 67e43eb2910..f50daf6b76d 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.demo;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@@ -78,8 +79,9 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
- public IServerInterceptor loggingInterceptor() {
+ public LoggingInterceptor loggingInterceptor() {
return FhirServerConfigCommon.loggingInterceptor();
}
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigCommon.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigCommon.java
index 63e7f5d7059..eb26118ae04 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigCommon.java
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigCommon.java
@@ -11,7 +11,6 @@ import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
-import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@@ -101,8 +100,9 @@ public class FhirServerConfigCommon {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
- public static IServerInterceptor loggingInterceptor() {
+ public static LoggingInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
index b35fdf54f99..80a74800a2a 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
@@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.demo;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@@ -82,8 +83,9 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
- public IServerInterceptor loggingInterceptor() {
+ public LoggingInterceptor loggingInterceptor() {
return FhirServerConfigCommon.loggingInterceptor();
}
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
index b6c86dc655b..8cda7234e28 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
@@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
+import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.api.EncodingEnum;
@@ -61,8 +62,8 @@ public class JpaServerDemo extends RestfulServer {
} else {
throw new IllegalStateException();
}
- List beans = myAppCtx.getBean(resourceProviderBeanName, List.class);
- setResourceProviders(beans);
+ ResourceProviderFactory beans = myAppCtx.getBean(resourceProviderBeanName, ResourceProviderFactory.class);
+ registerProviders(beans.createProviders());
/*
* The system provider implements non-resource-type methods, such as
@@ -76,7 +77,7 @@ public class JpaServerDemo extends RestfulServer {
} else {
throw new IllegalStateException();
}
- setPlainProviders(systemProvider);
+ registerProviders(systemProvider);
/*
* The conformance provider exports the supported resources, search parameters, etc for
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java
index 3c802e6cb10..1ed8720d067 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java
@@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
+import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.api.EncodingEnum;
@@ -61,8 +62,8 @@ public class JpaServerDemoDstu2 extends RestfulServer {
} else {
throw new IllegalStateException();
}
- List beans = myAppCtx.getBean(resourceProviderBeanName, List.class);
- setResourceProviders(beans);
+ ResourceProviderFactory beans = myAppCtx.getBean(resourceProviderBeanName, ResourceProviderFactory.class);
+ registerProviders(beans.createProviders());
/*
* The system provider implements non-resource-type methods, such as
@@ -76,7 +77,7 @@ public class JpaServerDemoDstu2 extends RestfulServer {
} else {
throw new IllegalStateException();
}
- setPlainProviders(systemProvider);
+ registerProvider(systemProvider);
/*
* The conformance provider exports the supported resources, search parameters, etc for
diff --git a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index c5150eac986..1783d9cbe99 100644
--- a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -7,7 +7,6 @@ import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
-import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -94,8 +93,9 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
- public IServerInterceptor loggingInterceptor() {
+ public LoggingInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
diff --git a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
index 93ae03951b4..0e5ded73bf6 100644
--- a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
+++ b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
@@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
+import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
@@ -47,14 +48,14 @@ public class JpaServerDemo extends RestfulServer {
* file which is automatically generated as a part of hapi-fhir-jpaserver-base and
* contains bean definitions for a resource provider for each resource type
*/
- List beans = myAppCtx.getBean("myResourceProvidersDstu3", List.class);
- setResourceProviders(beans);
+ ResourceProviderFactory beans = myAppCtx.getBean("myResourceProvidersDstu3", ResourceProviderFactory.class);
+ registerProviders(beans.createProviders());
/*
* The system provider implements non-resource-type methods, such as
* transaction, and global history.
*/
- setPlainProviders(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class));
+ registerProviders(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class));
/*
* The conformance provider exports the supported resources, search parameters, etc for
diff --git a/examples/src/main/java/example/AuthorizationInterceptors.java b/examples/src/main/java/example/AuthorizationInterceptors.java
index 28d508a5b38..009b725a11d 100644
--- a/examples/src/main/java/example/AuthorizationInterceptors.java
+++ b/examples/src/main/java/example/AuthorizationInterceptors.java
@@ -1,5 +1,8 @@
package example;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
@@ -13,6 +16,7 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
+import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -101,7 +105,8 @@ public class AuthorizationInterceptors {
@IdParam IdDt theId,
@ResourceParam Patient theResource,
@ConditionalUrlParam String theConditionalUrl,
- RequestDetails theRequestDetails) {
+ ServletRequestDetails theRequestDetails,
+ IInterceptorBroadcaster theInterceptorBroadcaster) {
// If we're processing a conditional URL...
if (isNotBlank(theConditionalUrl)) {
@@ -111,20 +116,25 @@ public class AuthorizationInterceptors {
// and supply the actual ID that's being updated
IdDt actual = new IdDt("Patient", "1123");
- // There are a number of possible constructors for ActionRequestDetails.
- // You should supply as much detail about the sub-operation as possible
- IServerInterceptor.ActionRequestDetails subRequest =
- new IServerInterceptor.ActionRequestDetails(theRequestDetails, actual);
-
- // Notify the interceptors
- subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE);
}
// In a real server, perhaps we would process the conditional
// request differently and follow a separate path. Either way,
// let's pretend there is some storage code here.
-
theResource.setId(theId.withVersion("2"));
+
+ // Notify the interceptor framework when we're about to perform an update. This is
+ // useful as the authorization interceptor will pick this event up and use it
+ // to factor into a decision about whether the operation should be allowed to proceed.
+ IBaseResource previousContents = theResource;
+ IBaseResource newContents = theResource;
+ HookParams params = new HookParams()
+ .add(IBaseResource.class, previousContents)
+ .add(IBaseResource.class, newContents)
+ .add(RequestDetails.class, theRequestDetails)
+ .add(ServletRequestDetails.class, theRequestDetails);
+ theInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, params);
+
MethodOutcome retVal = new MethodOutcome();
retVal.setCreated(true);
retVal.setResource(theResource);
diff --git a/examples/src/main/java/example/NewInterceptors.java b/examples/src/main/java/example/NewInterceptors.java
new file mode 100644
index 00000000000..60da5d43ded
--- /dev/null
+++ b/examples/src/main/java/example/NewInterceptors.java
@@ -0,0 +1,4 @@
+package example;
+
+public class NewInterceptors {
+}
diff --git a/examples/src/main/java/example/interceptor/MyTestInterceptor.java b/examples/src/main/java/example/interceptor/MyTestInterceptor.java
index ba4b92f4aaf..bd62ea36b64 100644
--- a/examples/src/main/java/example/interceptor/MyTestInterceptor.java
+++ b/examples/src/main/java/example/interceptor/MyTestInterceptor.java
@@ -1,8 +1,8 @@
package example.interceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Hook;
-import ca.uhn.fhir.jpa.model.interceptor.api.Interceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.Hook;
+import ca.uhn.fhir.interceptor.api.Interceptor;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage;
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Hook.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Hook.java
similarity index 67%
rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Hook.java
rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Hook.java
index ea89c412852..e819fb319ba 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Hook.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Hook.java
@@ -1,8 +1,8 @@
-package ca.uhn.fhir.jpa.model.interceptor.api;
+package ca.uhn.fhir.interceptor.api;
/*-
* #%L
- * HAPI FHIR Model
+ * HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
@@ -27,12 +27,8 @@ import java.lang.annotation.Target;
/**
* This annotation should be placed on
- * {@link Interceptor Subscription Interceptor}
+ * {@link Interceptor}
* bean methods.
- *
- * Methods with this annotation are invoked immediately before a REST HOOK
- * subscription delivery
- *
*
* @see Interceptor
*/
@@ -43,6 +39,15 @@ public @interface Hook {
/**
* Provides the specific point where this method should be invoked
*/
- Pointcut[] value();
+ Pointcut value();
+ /**
+ * The order that interceptors should be called in. Lower numbers happen before higher numbers. Default is 0
+ * and allowable values can be positive or negative or 0.
+ *
+ * If no order is specified, or the order is set to 0 (the default order),
+ * the order specified at the interceptor type level will take precedence.
+ *
+ */
+ int order() default Interceptor.DEFAULT_ORDER;
}
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/HookParams.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/HookParams.java
similarity index 79%
rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/HookParams.java
rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/HookParams.java
index 736c2200c7a..3aae13d7e04 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/HookParams.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/HookParams.java
@@ -1,8 +1,8 @@
-package ca.uhn.fhir.jpa.model.interceptor.api;
+package ca.uhn.fhir.interceptor.api;
/*-
* #%L
- * HAPI FHIR Model
+ * HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
@@ -25,6 +25,7 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
import org.apache.commons.lang3.Validate;
+import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -51,22 +52,23 @@ public class HookParams {
}
@SuppressWarnings("unchecked")
- private void add(T theNext) {
+ public HookParams add(@Nonnull T theNext) {
Class nextClass = (Class) theNext.getClass();
add(nextClass, theNext);
+ return this;
}
public HookParams add(Class theType, T theParam) {
return doAdd(theType, theParam);
}
- /**
- * This is useful for providing a lazy-loaded (generally expensive to create)
- * parameters
- */
- public HookParams addSupplier(Class theType, Supplier theParam) {
- return doAdd(theType, theParam);
- }
+// /**
+// * This is useful for providing a lazy-loaded (generally expensive to create)
+// * parameters
+// */
+// public HookParams addSupplier(Class theType, Supplier theParam) {
+// return doAdd(theType, theParam);
+// }
private HookParams doAdd(Class theType, Object theParam) {
Validate.isTrue(theType.equals(Supplier.class) == false, "Can not add parameters of type Supplier");
@@ -78,7 +80,7 @@ public class HookParams {
return get(theParamType, 0);
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("unchecked")
public T get(Class theParamType, int theIndex) {
List
+ */
+public enum Pointcut {
+
+ /**
+ * Registry Hook:
+ * This pointcut will be called once when a given interceptor is registered
+ */
+ INTERCEPTOR_REGISTERED(void.class),
+
+ /**
+ * Client Hook:
+ * This hook is called before an HTTP client request is sent
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the request
+ *
+ *
+ *
+ * Hook methods must return void.
+ */
+ CLIENT_REQUEST(void.class,
+ "ca.uhn.fhir.rest.client.api.IHttpRequest"
+ ),
+
+ /**
+ * Client Hook:
+ * This hook is called after an HTTP client request has completed, prior to returning
+ * the results to the calling code. Hook methods may modify the response.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the request
+ * ca.uhn.fhir.rest.client.api.IHttpRequest - The details of the response
+ *
+ *
+ *
+ * Hook methods must return void.
+ */
+ CLIENT_RESPONSE(void.class,
+ "ca.uhn.fhir.rest.client.api.IHttpRequest",
+ "ca.uhn.fhir.rest.client.api.IHttpResponse"
+ ),
+
+ /**
+ * Server Hook:
+ * This hook is called before any other processing takes place for each incoming request. It may be used to provide
+ * alternate handling for some requests, or to screen requests before they are handled, etc.
+ *
+ * Note that any exceptions thrown by this method will not be trapped by HAPI (they will be passed up to the server)
+ *
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
+ *
+ *
+ * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
+ *
+ *
+ *
+ * Hook methods may return true or void if processing should continue normally.
+ * This is generally the right thing to do. If your interceptor is providing a response rather than
+ * letting HAPI handle the response normally, you must return false. In this case,
+ * no further processing will occur and no further interceptors will be called.
+ */
+ SERVER_INCOMING_REQUEST_PRE_PROCESSED(boolean.class,
+ "javax.servlet.http.HttpServletRequest",
+ "javax.servlet.http.HttpServletResponse"
+ ),
+
+ /**
+ * Server Hook:
+ * This hook is invoked 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 also includes any {@link AuthenticationException}s
+ * thrown.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
+ *
+ *
+ * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
+ *
+ *
+ * ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException - The exception that was thrown
+ *
+ *
+ *
+ *
+ * Implementations of this method may choose to ignore/log/count/etc exceptions, and return true or
+ * void. 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 false, to indicate that they have handled the request and processing should stop.
+ *
+ */
+ SERVER_HANDLE_EXCEPTION(boolean.class,
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
+ "javax.servlet.http.HttpServletRequest",
+ "javax.servlet.http.HttpServletResponse",
+ "ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException"
+ ),
+
+ /**
+ * Server Hook:
+ * This method is called just before the actual implementing server method is invoked.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
+ *
+ *
+ * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
+ *
+ *
+ *
+ * Hook methods may return true or void if processing should continue normally.
+ * This is generally the right thing to do.
+ * If your interceptor is providing an HTTP response rather than letting HAPI handle the response normally, you
+ * must return false. In this case, no further processing will occur and no further interceptors
+ * will be called.
+ *
+ *
+ * Hook methods may also throw {@link AuthenticationException} if they would like. 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.
+ */
+ SERVER_INCOMING_REQUEST_POST_PROCESSED(boolean.class,
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
+ "javax.servlet.http.HttpServletRequest",
+ "javax.servlet.http.HttpServletResponse"
+ ),
+
+
+ /**
+ * Server Hook:
+ * This hook is invoked before an incoming request is processed. Note that this method is called
+ * after the server has begin preparing the response to the incoming client request.
+ * As such, it is not able to supply a response to the incoming request in the way that
+ * SERVER_INCOMING_REQUEST_PRE_HANDLED and
+ * {@link #SERVER_INCOMING_REQUEST_POST_PROCESSED}
+ * are.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.api.RestOperationTypeEnum - The type of operation that the FHIR server has determined that the client is trying to invoke
+ *
+ *
+ * ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails - An object which will be populated with the details which were extracted from the raw request by the
+ * server, e.g. the FHIR operation type and the parsed resource body (if any).
+ *
+ *
+ *
+ *
+ * Hook methods must return void
+ *
+ *
+ * Hook methods method may throw a subclass of {@link BaseServerResponseException}, and processing
+ * will be aborted with an appropriate error returned to the client.
+ *
+ */
+ SERVER_INCOMING_REQUEST_PRE_HANDLED(void.class,
+ "ca.uhn.fhir.rest.api.RestOperationTypeEnum",
+ "ca.uhn.fhir.rest.server.interceptor.IServerInterceptor$ActionRequestDetails"
+ ),
+
+ /**
+ * Server Hook:
+ * 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 hook 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 #SERVER_HANDLE_EXCEPTION} 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 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.
+ *
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ * java.lang.Throwable - The exception that was thrown. This will often be an instance of
+ * {@link BaseServerResponseException} but will not necessarily be one (e.g. it could be a
+ * {@link NullPointerException} in the case of a bug being triggered.
+ *
+ *
+ * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
+ *
+ *
+ * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
+ *
+ *
+ *
+ * Hook methods may return a new exception to use for processing, or null if this interceptor is not trying to
+ * modify the exception. For example, if this interceptor has nothing to do with exception processing, it
+ * should always return null. If this interceptor adds an OperationOutcome to the exception, it
+ * should return an exception.
+ *
+ */
+ SERVER_PRE_PROCESS_OUTGOING_EXCEPTION(BaseServerResponseException.class,
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
+ "java.lang.Throwable",
+ "javax.servlet.http.HttpServletRequest",
+ "javax.servlet.http.HttpServletResponse"
+ ),
+
+ /**
+ * Server Hook:
+ * This method is called after the server implementation method has been called, but before any attempt
+ * to stream the response back to the client. Interceptors may examine or modify the response before it
+ * is returned, or even prevent the response.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ * org.hl7.fhir.instance.model.api.IBaseResource - The resource that will be returned. This parameter may be null for some responses.
+ *
+ *
+ * ca.uhn.fhir.rest.api.server.ResponseDetails - This object contains details about the response, including the contents. Hook methods may modify this object to change or replace the response.
+ *
+ *
+ * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
+ *
+ *
+ * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
+ *
+ *
+ *
+ *
+ * Hook methods may return true or void if processing should continue normally.
+ * This is generally the right thing to do. If your interceptor is providing a response rather than
+ * letting HAPI handle the response normally, you must return false. In this case,
+ * no further processing will occur and no further interceptors will be called.
+ *
+ *
+ * Hook methods may also throw {@link AuthenticationException} 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.
+ */
+ SERVER_OUTGOING_RESPONSE(boolean.class,
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.ResponseDetails",
+ "javax.servlet.http.HttpServletRequest",
+ "javax.servlet.http.HttpServletResponse"
+ ),
+
+ /**
+ * Server Hook:
+ * This method is called after all processing is completed for a request, but only if the
+ * request completes normally (i.e. no exception is thrown).
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the request. This will be null if the server is not deployed to a RestfulServer environment.
+ *
+ *
+ *
+ *
+ * This method must return void
+ *
+ *
+ * This method should not throw any exceptions. Any exception that is thrown by this
+ * method will be logged, but otherwise not acted upon (i.e. even if a hook method
+ * throws an exception, processing will continue and other interceptors will be
+ * called). Therefore it is considered a bug to throw an exception from hook methods using this
+ * pointcut.
+ *
+ */
+ SERVER_PROCESSING_COMPLETED_NORMALLY(
+ void.class,
+ new ExceptionHandlingSpec()
+ .addLogAndSwallow(Throwable.class),
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+ /**
+ * Invoked whenever a persisted resource has been modified and is being submitted to the
+ * subscription processing pipeline. This method is called before the resource is placed
+ * on any queues for processing and executes synchronously during the resource modification
+ * operation itself, so it should return quickly.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.
+ *
+ *
+ *
+ * Hooks may return void or may return a boolean. If the method returns
+ * void or true, processing will continue normally. If the method
+ * returns false, subscription processing will not proceed for the given resource;
+ *
+ */
+ SUBSCRIPTION_RESOURCE_MODIFIED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
+
+
+ /**
+ * Invoked any time that a resource is matched by an individual subscription, and
+ * is about to be queued for delivery.
+ *
+ * Hooks may make changes to the delivery payload, or make changes to the
+ * canonical subscription such as adding headers, modifying the channel
+ * endpoint, etc.
+ *
+ * Hooks may accept the following parameters:
+ *
+ * Hooks may return void or may return a boolean. If the method returns
+ * void or true, processing will continue normally. If the method
+ * returns false, delivery will be aborted.
+ *
+ */
+ SUBSCRIPTION_RESOURCE_MATCHED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchResult"),
+
+
+ /**
+ * Invoked whenever a persisted resource was checked against all active subscriptions, and did not
+ * match any.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks should not modify this parameter as changes will not have any effect.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS(void.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
+
+ /**
+ * Invoked immediately before the delivery of a subscription, and right before any channel-specific
+ * hooks are invoked (e.g. {@link #SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY}.
+ *
+ * Hooks may make changes to the delivery payload, or make changes to the
+ * canonical subscription such as adding headers, modifying the channel
+ * endpoint, etc.
+ *
+ * Hooks may accept the following parameters:
+ *
+ * Hooks may return void or may return a boolean. If the method returns
+ * void or true, processing will continue normally. If the method
+ * returns false, processing will be aborted.
+ *
+ */
+ SUBSCRIPTION_BEFORE_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
+
+ /**
+ * Invoked immediately after the delivery of a subscription, and right before any channel-specific
+ * hooks are invoked (e.g. {@link #SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY}.
+ *
+ * Hooks may accept the following parameters:
+ *
+ */
+ SUBSCRIPTION_AFTER_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
+
+ /**
+ * Invoked immediately after the attempted delivery of a subscription, if the delivery
+ * failed.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ *
java.lang.Exception - The exception that caused the failure. Note this could be an exception thrown by a SUBSCRIPTION_BEFORE_DELIVERY or SUBSCRIPTION_AFTER_DELIVERY interceptor
+ *
ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage - the message that triggered the exception
+ *
java.lang.Exception
+ *
+ *
+ * Hooks may return void or may return a boolean. If the method returns
+ * void or true, processing will continue normally, meaning that
+ * an exception will be thrown by the delivery mechanism. This typically means that the
+ * message will be returned to the processing queue. If the method
+ * returns false, processing will be aborted and no further action will be
+ * taken for the delivery.
+ *
+ */
+ SUBSCRIPTION_AFTER_DELIVERY_FAILED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "java.lang.Exception"),
+
+ /**
+ * Invoked immediately after the delivery of a REST HOOK subscription.
+ *
+ * When this hook is called, all processing is complete so this hook should not
+ * make any changes to the parameters.
+ *
+ * Hooks may accept the following parameters:
+ *
+ */
+ SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
+
+ /**
+ * Invoked immediately before the delivery of a REST HOOK subscription.
+ *
+ * Hooks may make changes to the delivery payload, or make changes to the
+ * canonical subscription such as adding headers, modifying the channel
+ * endpoint, etc.
+ *
+ * Hooks may accept the following parameters:
+ *
+ * Hooks may return void or may return a boolean. If the method returns
+ * void or true, processing will continue normally. If the method
+ * returns false, processing will be aborted.
+ *
+ */
+ SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
+
+
+ /**
+ * Invoked whenever a persisted resource (a resource that has just been stored in the
+ * database via a create/update/patch/etc.) is about to be checked for whether any subscriptions
+ * were triggered as a result of the operation.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.
+ *
+ *
+ *
+ * Hooks may return void or may return a boolean. If the method returns
+ * void or true, processing will continue normally. If the method
+ * returns false, processing will be aborted.
+ *
+ */
+ SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED(boolean.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
+
+ /**
+ * Invoked whenever a persisted resource (a resource that has just been stored in the
+ * database via a create/update/patch/etc.) has been checked for whether any subscriptions
+ * were triggered as a result of the operation.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - This parameter should not be modified as processing is complete when this hook is invoked.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
+
+ /**
+ * Invoked immediately after an active subscription is "registered". In HAPI FHIR, when
+ * a subscription
+ *
+ * Hooks may make changes to the canonicalized subscription and this will have an effect
+ * on processing across this server. Note however that timing issues may occur, since the
+ * subscription is already technically live by the time this hook is called.
+ *
+ * Hooks may accept the following parameters:
+ *
+ */
+ SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED(void.class, "ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription"),
+
+ /**
+ * Invoked when a resource may be returned to the user, whether as a part of a READ,
+ * a SEARCH, or even as the response to a CREATE/UPDATE, etc.
+ *
+ * This hook is invoked when a resource has been loaded by the storage engine and
+ * is being returned to the HTTP stack for response. This is not a guarantee that the
+ * client will ultimately see it, since filters/headers/etc may affect what
+ * is returned but if a resource is loaded it is likely to be used.
+ * Note also that caching may affect whether this pointcut is invoked.
+ *
+ *
+ * Hooks will have access to the contents of the resource being returned
+ * and may choose to make modifications. These changes will be reflected in
+ * returned resource but have no effect on storage.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
org.hl7.fhir.instance.model.api.IBaseResource - The resource being returned
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred. Note that this parameter may be null in contexts where the request is not
+ * known, such as while processing searches
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PREACCESS_RESOURCE(void.class,
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+ /**
+ * Invoked before a resource will be created, immediately before the resource
+ * is persisted to the database.
+ *
+ * Hooks will have access to the contents of the resource being created
+ * and may choose to make modifications to it. These changes will be
+ * reflected in permanent storage.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
org.hl7.fhir.instance.model.api.IBaseResource
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PRESTORAGE_RESOURCE_CREATED(void.class,
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+ /**
+ * Invoked before a resource will be created, immediately before the transaction
+ * is committed (after all validation and other business rules have successfully
+ * completed, and any other database activity is complete.
+ *
+ * Hooks will have access to the contents of the resource being created
+ * but should generally not make any
+ * changes as storage has already occurred. Changes will not be reflected
+ * in storage, but may be reflected in the HTTP response.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
org.hl7.fhir.instance.model.api.IBaseResource
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PRECOMMIT_RESOURCE_CREATED(void.class,
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+ /**
+ * Invoked before a resource will be created
+ *
+ * Hooks will have access to the contents of the resource being deleted
+ * but should not make any changes as storage has already occurred
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
org.hl7.fhir.instance.model.api.IBaseResource - The resource being deleted
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PRECOMMIT_RESOURCE_DELETED(void.class,
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+
+ /**
+ * Invoked before a resource will be updated, immediately before the transaction
+ * is committed (after all validation and other business rules have successfully
+ * completed, and any other database activity is complete.
+ *
+ * Hooks will have access to the contents of the resource being updated
+ * (both the previous and new contents) but should generally not make any
+ * changes as storage has already occurred. Changes will not be reflected
+ * in storage, but may be reflected in the HTTP response.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
org.hl7.fhir.instance.model.api.IBaseResource - The previous contents of the resource
+ *
org.hl7.fhir.instance.model.api.IBaseResource - The proposed new new contents of the resource
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PRECOMMIT_RESOURCE_UPDATED(void.class,
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+ /**
+ * Invoked before a resource will be updated, immediately before the resource
+ * is persisted to the database.
+ *
+ * Hooks will have access to the contents of the resource being updated
+ * (both the previous and new contents) and may choose to make modifications
+ * to the new contents of the resource. These changes will be reflected in
+ * permanent storage.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
org.hl7.fhir.instance.model.api.IBaseResource - The previous contents of the resource being updated
+ *
org.hl7.fhir.instance.model.api.IBaseResource - The new contents of the resource being updated
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PRESTORAGE_RESOURCE_UPDATED(void.class,
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+ /**
+ * Invoked before a resource will be created, immediately before the resource
+ * is persisted to the database.
+ *
+ * Hooks will have access to the contents of the resource being created
+ * and may choose to make modifications to it. These changes will be
+ * reflected in permanent storage.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
org.hl7.fhir.instance.model.api.IBaseResource - The resource being deleted
+ *
+ * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. Note that the bean
+ * properties are not all guaranteed to be populated, depending on how early during processing the
+ * exception occurred.
+ *
+ *
+ * ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
+ * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
+ * pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
+ * only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PRESTORAGE_RESOURCE_DELETED(void.class,
+ "org.hl7.fhir.instance.model.api.IBaseResource",
+ "ca.uhn.fhir.rest.api.server.RequestDetails",
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
+ ),
+
+ /**
+ * Note that this is a performance tracing hook. Use with caution in production
+ * systems, since calling it may (or may not) carry a cost.
+ *
+ * This hook is invoked when any informational or warning messages generated by the
+ * SearchCoordinator are created. It is typically used to provide logging
+ * or capture details related to a specific request.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.jpa.model.search.StorageProcessingMessage - Contains the message
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ STORAGE_PROCESSING_MESSAGE(void.class,
+ "ca.uhn.fhir.jpa.model.search.StorageProcessingMessage"
+ ),
+
+ /**
+ * Note that this is a performance tracing hook. Use with caution in production
+ * systems, since calling it may (or may not) carry a cost.
+ *
+ * This hook is invoked when a search has returned the very first result
+ * from the database. The timing on this call can be a good indicator of how
+ * performant a query is in general.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
+ * performed. Hooks should not modify this object.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED(void.class, "ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails"),
+
+ /**
+ * Note that this is a performance tracing hook. Use with caution in production
+ * systems, since calling it may (or may not) carry a cost.
+ *
+ * This hook is invoked when an individual search query SQL SELECT statement
+ * has completed and no more results are available from that query. Note that this
+ * doesn't necessarily mean that no more matching results exist in the database,
+ * since HAPI FHIR JPA batch loads results in to the query cache in chunks in order
+ * to provide predicable results without overloading memory or the database.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
+ * performed. Hooks should not modify this object.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ JPA_PERFTRACE_SEARCH_SELECT_COMPLETE(void.class, "ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails"),
+
+ /**
+ * Note that this is a performance tracing hook. Use with caution in production
+ * systems, since calling it may (or may not) carry a cost.
+ *
+ * This hook is invoked when a search has failed for any reason. When this pointcut
+ * is invoked, the search has completed unsuccessfully and will not be continued.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
+ * performed. Hooks should not modify this object.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ JPA_PERFTRACE_SEARCH_FAILED(void.class, "ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails"),
+
+ /**
+ * Note that this is a performance tracing hook. Use with caution in production
+ * systems, since calling it may (or may not) carry a cost.
+ *
+ * This hook is invoked when a search has failed for any reason. When this pointcut
+ * is invoked, a pass in the Search Coordinator has completed successfully, but
+ * not all possible resources have been loaded yet so a future paging request
+ * may trigger a new task that will load further resources.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
+ * performed. Hooks should not modify this object.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ JPA_PERFTRACE_SEARCH_PASS_COMPLETE(void.class, "ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails"),
+
+ /**
+ * Note that this is a performance tracing hook. Use with caution in production
+ * systems, since calling it may (or may not) carry a cost.
+ *
+ * This hook is invoked when a search has failed for any reason. When this pointcut
+ * is invoked, a pass in the Search Coordinator has completed successfully, and all
+ * possible results have been fetched and loaded into the query cache.
+ *
+ * Hooks may accept the following parameters:
+ *
+ *
+ * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
+ * performed. Hooks should not modify this object.
+ *
+ *
+ *
+ * Hooks should return void.
+ *
+ */
+ JPA_PERFTRACE_SEARCH_COMPLETE(void.class, "ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails"),
+
+
+ /**
+ * This pointcut is used only for unit tests. Do not use in production code as it may be changed or
+ * removed at any time.
+ */
+ TEST_RB(
+ boolean.class,
+ new ExceptionHandlingSpec().addLogAndSwallow(IllegalStateException.class),
+ String.class.getName(),
+ String.class.getName()),
+
+ /**
+ * This pointcut is used only for unit tests. Do not use in production code as it may be changed or
+ * removed at any time.
+ */
+ TEST_RO(BaseServerResponseException.class, String.class.getName(), String.class.getName());
+
+ private final List myParameterTypes;
+ private final Class> myReturnType;
+ private final ExceptionHandlingSpec myExceptionHandlingSpec;
+
+ Pointcut(@Nonnull Class> theReturnType, String... theParameterTypes) {
+ this(theReturnType, new ExceptionHandlingSpec(), theParameterTypes);
+ }
+
+ Pointcut(@Nonnull Class> theReturnType, @Nonnull ExceptionHandlingSpec theExceptionHandlingSpec, String... theParameterTypes) {
+ myReturnType = theReturnType;
+ myExceptionHandlingSpec = theExceptionHandlingSpec;
+ myParameterTypes = Collections.unmodifiableList(Arrays.asList(theParameterTypes));
+ }
+
+ public boolean isShouldLogAndSwallowException(@Nonnull Throwable theException) {
+ for (Class extends Throwable> next : myExceptionHandlingSpec.myTypesToLogAndSwallow) {
+ if (next.isAssignableFrom(theException.getClass())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nonnull
+ public Class> getReturnType() {
+ return myReturnType;
+ }
+
+ @Nonnull
+ public List getParameterTypes() {
+ return myParameterTypes;
+ }
+
+ private static class ExceptionHandlingSpec {
+
+ private final Set> myTypesToLogAndSwallow = new HashSet<>();
+
+ ExceptionHandlingSpec addLogAndSwallow(@Nonnull Class extends Throwable> theType) {
+ myTypesToLogAndSwallow.add(theType);
+ return this;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorService.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/InterceptorService.java
similarity index 69%
rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorService.java
rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/InterceptorService.java
index aa21c632223..d9a0c58a975 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorService.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/InterceptorService.java
@@ -1,17 +1,17 @@
-package ca.uhn.fhir.jpa.model.interceptor.executor;
+package ca.uhn.fhir.interceptor.executor;
/*-
* #%L
- * HAPI FHIR Model
+ * HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,26 +20,31 @@ package ca.uhn.fhir.jpa.model.interceptor.executor;
* #L%
*/
-import ca.uhn.fhir.jpa.model.interceptor.api.*;
+import ca.uhn.fhir.interceptor.api.*;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
import org.apache.commons.lang3.Validate;
-import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.core.annotation.Order;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
-public class InterceptorService implements IInterceptorRegistry, IInterceptorBroadcaster {
+public class InterceptorService implements IInterceptorService, IInterceptorBroadcaster {
+ private static final Logger ourLog = LoggerFactory.getLogger(InterceptorService.class);
private final List myInterceptors = new ArrayList<>();
private final ListMultimap myGlobalInvokers = ArrayListMultimap.create();
private final ListMultimap myAnonymousInvokers = ArrayListMultimap.create();
@@ -84,11 +89,10 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
return myInterceptors;
}
-
@Override
@VisibleForTesting
public void registerAnonymousInterceptor(Pointcut thePointcut, IAnonymousInterceptor theInterceptor) {
- registerAnonymousInterceptor(thePointcut, DEFAULT_ORDER, theInterceptor);
+ registerAnonymousInterceptor(thePointcut, Interceptor.DEFAULT_ORDER, theInterceptor);
}
public void setName(String theName) {
@@ -100,15 +104,30 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
Validate.notNull(thePointcut);
Validate.notNull(theInterceptor);
synchronized (myRegistryMutex) {
+
myAnonymousInvokers.put(thePointcut, new AnonymousLambdaInvoker(thePointcut, theInterceptor, theOrder));
+ if (!isInterceptorAlreadyRegistered(theInterceptor)) {
+ myInterceptors.add(theInterceptor);
+ }
+ }
+ }
+
+ @Override
+ public List getAllRegisteredInterceptors() {
+ synchronized (myRegistryMutex) {
+ List retVal = new ArrayList<>();
+ retVal.addAll(myInterceptors);
+ return Collections.unmodifiableList(retVal);
}
}
@Override
@VisibleForTesting
- public void clearAnonymousHookForUnitTest() {
+ public void unregisterAllInterceptors() {
synchronized (myRegistryMutex) {
myAnonymousInvokers.clear();
+ myGlobalInvokers.clear();
+ myInterceptors.clear();
}
}
@@ -119,6 +138,13 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
}
}
+ @Override
+ public void registerInterceptors(@Nullable Collection> theInterceptors) {
+ if (theInterceptors != null) {
+ theInterceptors.forEach(t -> registerInterceptor(t));
+ }
+ }
+
@Override
public boolean registerThreadLocalInterceptor(Object theInterceptor) {
if (!myThreadlocalInvokersEnabled) {
@@ -160,6 +186,7 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
List addedInvokers = scanInterceptorAndAddToInvokerMultimap(theInterceptor, myGlobalInvokers);
if (addedInvokers.isEmpty()) {
+ ourLog.warn("Interceptor registered with no valid hooks - Type was: {}", theInterceptor.getClass().getName());
return false;
}
@@ -189,21 +216,11 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
}
}
- @Override
- public boolean registerGlobalInterceptor(Object theInterceptor) {
- return registerInterceptor(theInterceptor);
- }
-
- @Override
- public void unregisterGlobalInterceptor(Object theInterceptor) {
- unregisterInterceptor(theInterceptor);
- }
-
private void sortByOrderAnnotation(List theObjects) {
IdentityHashMap interceptorToOrder = new IdentityHashMap<>();
for (Object next : theObjects) {
- Order orderAnnotation = next.getClass().getAnnotation(Order.class);
- int order = orderAnnotation != null ? orderAnnotation.value() : 0;
+ Interceptor orderAnnotation = next.getClass().getAnnotation(Interceptor.class);
+ int order = orderAnnotation != null ? orderAnnotation.order() : 0;
interceptorToOrder.put(next, order);
}
@@ -215,27 +232,47 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
}
@Override
- public boolean callHooks(Pointcut thePointcut, Object... theParams) {
- return callHooks(thePointcut, new HookParams(theParams));
+ public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
+ assert haveAppropriateParams(thePointcut, theParams);
+ assert thePointcut.getReturnType() != void.class && thePointcut.getReturnType() != boolean.class;
+
+ Object retVal = doCallHooks(thePointcut, theParams, null);
+ return retVal;
}
@Override
public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
assert haveAppropriateParams(thePointcut, theParams);
+ assert thePointcut.getReturnType() == void.class || thePointcut.getReturnType() == boolean.class;
+ Object retValObj = doCallHooks(thePointcut, theParams, true);
+ return (Boolean) retValObj;
+ }
+
+ private Object doCallHooks(Pointcut thePointcut, HookParams theParams, Object theRetVal) {
List invokers = getInvokersForPointcut(thePointcut);
/*
* Call each hook in order
*/
for (BaseInvoker nextInvoker : invokers) {
- boolean shouldContinue = nextInvoker.invoke(theParams);
- if (!shouldContinue) {
- return false;
+ Object nextOutcome = nextInvoker.invoke(theParams);
+ if (thePointcut.getReturnType() == boolean.class) {
+ Boolean nextOutcomeAsBoolean = (Boolean) nextOutcome;
+ if (Boolean.FALSE.equals(nextOutcomeAsBoolean)) {
+ ourLog.trace("callHooks({}) for invoker({}) returned false", thePointcut, nextInvoker);
+ theRetVal = false;
+ break;
+ }
+ } else if (thePointcut.getReturnType() != void.class) {
+ if (nextOutcome != null) {
+ theRetVal = nextOutcome;
+ break;
+ }
}
}
- return true;
+ return theRetVal;
}
@VisibleForTesting
@@ -272,7 +309,8 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
/**
* First argument must be the global invoker list!!
*/
- private List union(List... theInvokersLists) {
+ @SafeVarargs
+ private final List union(List... theInvokersLists) {
List haveOne = null;
boolean haveMultiple = false;
for (List nextInvokerList : theInvokersLists) {
@@ -322,7 +360,7 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
* Only call this when assertions are enabled, it's expensive
*/
boolean haveAppropriateParams(Pointcut thePointcut, HookParams theParams) {
- Validate.isTrue(theParams.getParamsForType().values().size() == thePointcut.getParameterTypes().size(), "Wrong number of params for pointcut %s - Wanted %s but found %s", thePointcut.name(), toErrorString(thePointcut.getParameterTypes()), theParams.getParamsForType().values().stream().map(t -> t.getClass().getSimpleName()).sorted().collect(Collectors.toList()));
+ Validate.isTrue(theParams.getParamsForType().values().size() == thePointcut.getParameterTypes().size(), "Wrong number of params for pointcut %s - Wanted %s but found %s", thePointcut.name(), toErrorString(thePointcut.getParameterTypes()), theParams.getParamsForType().values().stream().map(t -> t != null ? t.getClass().getSimpleName() : "null").sorted().collect(Collectors.toList()));
List wantedTypes = new ArrayList<>(thePointcut.getParameterTypes());
@@ -330,8 +368,8 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
for (Class> nextTypeClass : givenTypes.keySet()) {
String nextTypeName = nextTypeClass.getName();
for (Object nextParamValue : givenTypes.get(nextTypeClass)) {
- Validate.isTrue(nextTypeClass.isAssignableFrom(nextParamValue.getClass()), "Invalid params for pointcut %s - %s is not of type %s", thePointcut.name(), nextParamValue.getClass(), nextTypeClass);
- Validate.isTrue(wantedTypes.remove(nextTypeName), "Invalid params for pointcut %s - Wanted %s but missing %s", thePointcut.name(), toErrorString(thePointcut.getParameterTypes()), nextTypeName);
+ Validate.isTrue(nextParamValue == null || nextTypeClass.isAssignableFrom(nextParamValue.getClass()), "Invalid params for pointcut %s - %s is not of type %s", thePointcut.name(), nextParamValue != null ? nextParamValue.getClass() : "null", nextTypeClass);
+ Validate.isTrue(wantedTypes.remove(nextTypeName), "Invalid params for pointcut %s - Wanted %s but found %s", thePointcut.name(), toErrorString(thePointcut.getParameterTypes()), nextTypeName);
}
}
@@ -349,7 +387,7 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
}
@Override
- boolean invoke(HookParams theParams) {
+ Object invoke(HookParams theParams) {
myHook.invoke(myPointcut, theParams);
return true;
}
@@ -369,7 +407,7 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
return myInterceptor;
}
- abstract boolean invoke(HookParams theParams);
+ abstract Object invoke(HookParams theParams);
@Override
public int compareTo(BaseInvoker theInvoker) {
@@ -379,27 +417,27 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
private static class HookInvoker extends BaseInvoker {
- private final boolean myReturnsBoolean;
private final Method myMethod;
private final Class>[] myParameterTypes;
private final int[] myParameterIndexes;
- private final Set myPointcuts;
+ private final Pointcut myPointcut;
/**
* Constructor
*/
private HookInvoker(Hook theHook, @Nonnull Object theInterceptor, @Nonnull Method theHookMethod, int theOrder) {
super(theInterceptor, theOrder);
- myPointcuts = Collections.unmodifiableSet(Sets.newHashSet(theHook.value()));
+ myPointcut = theHook.value();
myParameterTypes = theHookMethod.getParameterTypes();
myMethod = theHookMethod;
Class> returnType = theHookMethod.getReturnType();
- if (returnType.equals(boolean.class)) {
- myReturnsBoolean = true;
+ if (myPointcut.getReturnType().equals(boolean.class)) {
+ Validate.isTrue(boolean.class.equals(returnType) || void.class.equals(returnType), "Method does not return boolean or void: %s", theHookMethod);
+ } else if (myPointcut.getReturnType().equals(void.class)) {
+ Validate.isTrue(void.class.equals(returnType), "Method does not return void: %s", theHookMethod);
} else {
- Validate.isTrue(void.class.equals(returnType), "Method does not return boolean or void: %s", theHookMethod);
- myReturnsBoolean = false;
+ Validate.isTrue(myPointcut.getReturnType().isAssignableFrom(returnType) || void.class.equals(returnType), "Method does not return %s or void: %s", myPointcut.getReturnType(), theHookMethod);
}
myParameterIndexes = new int[myParameterTypes.length];
@@ -408,17 +446,26 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
AtomicInteger counter = typeToCount.computeIfAbsent(myParameterTypes[i], t -> new AtomicInteger(0));
myParameterIndexes[i] = counter.getAndIncrement();
}
+
+ myMethod.setAccessible(true);
}
- public Set getPointcuts() {
- return myPointcuts;
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("method", myMethod)
+ .toString();
+ }
+
+ public Pointcut getPointcut() {
+ return myPointcut;
}
/**
* @return Returns true/false if the hook method returns a boolean, returns true otherwise
*/
@Override
- boolean invoke(HookParams theParams) {
+ Object invoke(HookParams theParams) {
Object[] args = new Object[myParameterTypes.length];
for (int i = 0; i < myParameterTypes.length; i++) {
@@ -430,18 +477,18 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
// Invoke the method
try {
- Object returnValue = myMethod.invoke(getInterceptor(), args);
- if (myReturnsBoolean) {
- return (boolean) returnValue;
- } else {
- return true;
- }
+ return myMethod.invoke(getInterceptor(), args);
} catch (InvocationTargetException e) {
Throwable targetException = e.getTargetException();
+ if (myPointcut.isShouldLogAndSwallowException(targetException)) {
+ ourLog.error("Exception thrown by interceptor: " + targetException.toString(), targetException);
+ return null;
+ }
+
if (targetException instanceof RuntimeException) {
throw ((RuntimeException) targetException);
} else {
- throw new InternalErrorException("Failure invoking interceptor for pointcut(s) " + getPointcuts(), targetException);
+ throw new InternalErrorException("Failure invoking interceptor for pointcut(s) " + getPointcut(), targetException);
}
} catch (Exception e) {
throw new InternalErrorException(e);
@@ -459,17 +506,16 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
// Invoke the REGISTERED pointcut for any added hooks
addedInvokers.stream()
- .filter(t -> t.getPointcuts().contains(Pointcut.REGISTERED))
+ .filter(t -> Pointcut.INTERCEPTOR_REGISTERED.equals(t.getPointcut()))
.forEach(t -> t.invoke(new HookParams()));
// Register the interceptor and its various hooks
for (HookInvoker nextAddedHook : addedInvokers) {
- for (Pointcut nextPointcut : nextAddedHook.getPointcuts()) {
- if (nextPointcut.equals(Pointcut.REGISTERED)) {
- continue;
- }
- theInvokers.put(nextPointcut, nextAddedHook);
+ Pointcut nextPointcut = nextAddedHook.getPointcut();
+ if (nextPointcut.equals(Pointcut.INTERCEPTOR_REGISTERED)) {
+ continue;
}
+ theInvokers.put(nextPointcut, nextAddedHook);
}
// Make sure we're always sorted according to the order declared in
@@ -488,27 +534,37 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
private static List scanInterceptorForHookMethods(Object theInterceptor, int theTypeOrder) {
ArrayList retVal = new ArrayList<>();
for (Method nextMethod : theInterceptor.getClass().getMethods()) {
- Hook hook = AnnotationUtils.findAnnotation(nextMethod, Hook.class);
+ Optional hook = findAnnotation(nextMethod, Hook.class);
- if (hook != null) {
+ if (hook.isPresent()) {
int methodOrder = theTypeOrder;
- Order methodOrderAnnotation = AnnotationUtils.findAnnotation(nextMethod, Order.class);
- if (methodOrderAnnotation != null) {
- methodOrder = methodOrderAnnotation.value();
+ int methodOrderAnnotation = hook.get().order();
+ if (methodOrderAnnotation != Interceptor.DEFAULT_ORDER) {
+ methodOrder = methodOrderAnnotation;
}
- retVal.add(new HookInvoker(hook, theInterceptor, nextMethod, methodOrder));
+ retVal.add(new HookInvoker(hook.get(), theInterceptor, nextMethod, methodOrder));
}
}
return retVal;
}
+ private static Optional findAnnotation(AnnotatedElement theObject, Class theHookClass) {
+ T annotation;
+ if (theObject instanceof Method) {
+ annotation = MethodUtils.getAnnotation((Method) theObject, theHookClass, true, true);
+ } else {
+ annotation = theObject.getAnnotation(theHookClass);
+ }
+ return Optional.ofNullable(annotation);
+ }
+
private static int determineOrder(Class> theInterceptorClass) {
- int typeOrder = DEFAULT_ORDER;
- Order typeOrderAnnotation = AnnotationUtils.findAnnotation(theInterceptorClass, Order.class);
- if (typeOrderAnnotation != null) {
- typeOrder = typeOrderAnnotation.value();
+ int typeOrder = Interceptor.DEFAULT_ORDER;
+ Optional typeOrderAnnotation = findAnnotation(theInterceptorClass, Interceptor.class);
+ if (typeOrderAnnotation.isPresent()) {
+ typeOrder = typeOrderAnnotation.get().order();
}
return typeOrder;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IClientInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IClientInterceptor.java
index 067c03527bd..f6ac70860f3 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IClientInterceptor.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IClientInterceptor.java
@@ -20,6 +20,9 @@ package ca.uhn.fhir.rest.client.api;
* #L%
*/
+import ca.uhn.fhir.interceptor.api.Hook;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+
import java.io.IOException;
/**
@@ -35,11 +38,13 @@ public interface IClientInterceptor {
/**
* Fired by the client just before invoking the HTTP client request
*/
+ @Hook(Pointcut.CLIENT_REQUEST)
void interceptRequest(IHttpRequest theRequest);
/**
* Fired by the client upon receiving an HTTP response, prior to processing that response
*/
+ @Hook(Pointcut.CLIENT_RESPONSE)
void interceptResponse(IHttpResponse theResponse) throws IOException;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java
index 0b1b7fd5904..c883feb9376 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java
@@ -1,12 +1,13 @@
package ca.uhn.fhir.rest.client.api;
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
-import java.util.List;
+import javax.annotation.Nonnull;
/*
* #%L
@@ -30,6 +31,20 @@ import java.util.List;
public interface IRestfulClient {
+ /**
+ * Sets the interfceptor service used by this client
+ *
+ * @since 3.8.0
+ */
+ IInterceptorService getInterceptorService();
+
+ /**
+ * Sets the interfceptor service used by this client
+ *
+ * @since 3.8.0
+ */
+ void setInterceptorService(@Nonnull IInterceptorService theInterceptorService);
+
/**
* Retrieve the contents at the given URL and parse them as a resource. This
* method could be used as a low level implementation of a read/vread/search
@@ -69,11 +84,6 @@ public interface IRestfulClient {
*/
IHttpClient getHttpClient();
- /**
- * Returns the client interceptors that have been registered with this client
- */
- List getInterceptors();
-
/**
* Base URL for the server, with no trailing "/"
*/
@@ -82,7 +92,10 @@ public interface IRestfulClient {
/**
* Register a new interceptor for this client. An interceptor can be used to add additional
* logging, or add security headers, or pre-process responses, etc.
+ *
+ * @deprecated Use {@link #getInterceptorService()} to access the list of inteerceptors, register them, and unregister them
*/
+ @Deprecated
void registerInterceptor(IClientInterceptor theInterceptor);
/**
@@ -102,7 +115,10 @@ public interface IRestfulClient {
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
+ *
+ * @deprecated Use {@link #getInterceptorService()} to access the list of inteerceptors, register them, and unregister them
*/
+ @Deprecated
void unregisterInterceptor(IClientInterceptor theInterceptor);
/**
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java
index 92836d62f0b..cd345512dc7 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PortUtil.java
@@ -150,16 +150,6 @@ public class PortUtil {
int nextCandidatePort = myCurrentControlSocketPort + myCurrentOffset;
// Try to open a port on this socket and use it
-// try (ServerSocket server = new ServerSocket()) {
-// server.setReuseAddress(true);
-// server.bind(new InetSocketAddress("localhost", nextCandidatePort));
-// try (Socket client = new Socket()) {
-// client.setReuseAddress(true);
-// client.connect(new InetSocketAddress("localhost", nextCandidatePort));
-// }
-// } catch (IOException e) {
-// continue;
-// }
if (!isAvailable(nextCandidatePort)) {
continue;
}
@@ -172,27 +162,6 @@ public class PortUtil {
.orElse(stackTraceElements[2]);
ourLog.info("Returned available port {} for: {}", nextCandidatePort, previousElement.toString());
-// /*
-// * This is an attempt to make sure the port is actually
-// * free before releasing it. For whatever reason on Linux
-// * it seems like even after we close the ServerSocket there
-// * is a short while where it is not possible to bind the
-// * port, even though it should be released by then.
-// *
-// * I don't have any solid evidence that this is a good
-// * way to do this, but it seems to help...
-// */
-// for (int i = 0; i < 10; i++) {
-// try (Socket client = new Socket()) {
-// client.setReuseAddress(true);
-// client.connect(new InetSocketAddress(nextCandidatePort), 1000);
-// ourLog.info("Socket still seems open");
-// Thread.sleep(250);
-// } catch (Exception e) {
-// break;
-// }
-// }
-//
try {
Thread.sleep(ourPortDelay);
} catch (InterruptedException theE) {
@@ -216,22 +185,37 @@ public class PortUtil {
}
}
- private static boolean isAvailable(int port) {
- ourLog.info("Testing a bind on port {}", port);
- try (ServerSocket ss = new ServerSocket(port)) {
+ /**
+ * This method checks if we are able to bind a given port to both
+ * 0.0.0.0 and localhost in order to be sure it's truly available.
+ */
+ private static boolean isAvailable(int thePort) {
+ ourLog.info("Testing a bind on thePort {}", thePort);
+ try (ServerSocket ss = new ServerSocket()) {
ss.setReuseAddress(true);
- try (DatagramSocket ds = new DatagramSocket(port)) {
+ ss.bind(new InetSocketAddress("0.0.0.0", thePort));
+ try (DatagramSocket ds = new DatagramSocket()) {
ds.setReuseAddress(true);
- ourLog.info("Successfully bound port {}", port);
- return true;
+ ds.connect(new InetSocketAddress("127.0.0.1", thePort));
+ ourLog.info("Successfully bound thePort {}", thePort);
} catch (IOException e) {
- ourLog.info("Failed to bind port {}: {}", port, e.toString());
+ ourLog.info("Failed to bind thePort {}: {}", thePort, e.toString());
return false;
}
} catch (IOException e) {
- ourLog.info("Failed to bind port {}: {}", port, e.toString());
+ ourLog.info("Failed to bind thePort {}: {}", thePort, e.toString());
return false;
}
+
+ try (ServerSocket ss = new ServerSocket()) {
+ ss.setReuseAddress(true);
+ ss.bind(new InetSocketAddress("localhost", thePort));
+ } catch (IOException e) {
+ ourLog.info("Failed to bind thePort {}: {}", thePort, e.toString());
+ return false;
+ }
+
+ return true;
}
/**
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/interceptor/executor/InterceptorServiceTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/interceptor/executor/InterceptorServiceTest.java
new file mode 100644
index 00000000000..bd9e8519996
--- /dev/null
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/interceptor/executor/InterceptorServiceTest.java
@@ -0,0 +1,588 @@
+package ca.uhn.fhir.interceptor.executor;
+
+import ca.uhn.fhir.interceptor.api.Hook;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.Interceptor;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
+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.util.StopWatch;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.*;
+
+public class InterceptorServiceTest {
+
+ private static final Logger ourLog = LoggerFactory.getLogger(InterceptorServiceTest.class);
+ private List myInvocations = new ArrayList<>();
+
+ @Test
+ public void testInterceptorWithAnnotationDefinedOnInterface() {
+
+ InterceptorService svc = new InterceptorService();
+ TestInterceptorWithAnnotationDefinedOnInterface_Class interceptor = new TestInterceptorWithAnnotationDefinedOnInterface_Class();
+ svc.registerInterceptor(interceptor);
+
+ assertEquals(1, interceptor.getRegisterCount());
+ }
+
+ @Test
+ public void testInterceptorThrowsException() {
+
+ class InterceptorThrowingException {
+ @Hook(Pointcut.TEST_RB)
+ public void test(String theValue) {
+ throw new AuthenticationException(theValue);
+ }
+ }
+
+ InterceptorService svc = new InterceptorService();
+ svc.registerInterceptor(new InterceptorThrowingException());
+
+ try {
+ svc.callHooks(Pointcut.TEST_RB, new HookParams("A MESSAGE", "B"));
+ fail();
+ } catch (AuthenticationException e) {
+ assertEquals("A MESSAGE", e.getMessage());
+ }
+
+ }
+
+ @Test
+ public void testInterceptorReturnsClass() {
+
+ class InterceptorReturningClass {
+
+ private BaseServerResponseException myNextResponse;
+
+ @Hook(Pointcut.TEST_RO)
+ public BaseServerResponseException hook() {
+ return myNextResponse;
+ }
+
+ }
+
+ InterceptorReturningClass interceptor0 = new InterceptorReturningClass();
+ InterceptorReturningClass interceptor1 = new InterceptorReturningClass();
+
+ InterceptorService svc = new InterceptorService();
+ svc.registerInterceptor(interceptor0);
+ svc.registerInterceptor(interceptor1);
+
+ interceptor0.myNextResponse = new InvalidRequestException("0");
+ interceptor1.myNextResponse = new InvalidRequestException("1");
+ Object response = svc.callHooksAndReturnObject(Pointcut.TEST_RO, new HookParams("", ""));
+ assertEquals("0", ((InvalidRequestException) response).getMessage());
+
+ interceptor0.myNextResponse = null;
+ response = svc.callHooksAndReturnObject(Pointcut.TEST_RO, new HookParams("", ""));
+ assertEquals("1", ((InvalidRequestException) response).getMessage());
+ }
+
+ @Test
+ public void testRegisterHookFails() {
+ InterceptorService svc = new InterceptorService();
+ int initialSize = svc.getGlobalInterceptorsForUnitTest().size();
+
+ try {
+ svc.registerInterceptor(new InterceptorThatFailsOnRegister());
+ fail();
+ } catch (InternalErrorException e) {
+ // good
+ }
+
+ assertEquals(initialSize, svc.getGlobalInterceptorsForUnitTest().size());
+
+ }
+
+ @Test
+ public void testManuallyRegisterInterceptor() {
+ InterceptorService svc = new InterceptorService();
+
+ // Registered in opposite order to verify that the order on the annotation is used
+ MyTestInterceptorTwo interceptor1 = new MyTestInterceptorTwo();
+ MyTestInterceptorOne interceptor0 = new MyTestInterceptorOne();
+ svc.registerInterceptor(interceptor1);
+ svc.registerInterceptor(interceptor0);
+
+ // Register the manual interceptor (has Order right in the middle)
+ MyTestInterceptorManual myInterceptorManual = new MyTestInterceptorManual();
+ svc.registerInterceptor(myInterceptorManual);
+ List globalInterceptors = svc.getGlobalInterceptorsForUnitTest();
+ assertEquals(3, globalInterceptors.size());
+ assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
+ assertTrue(globalInterceptors.get(1).getClass().toString(), globalInterceptors.get(1) instanceof MyTestInterceptorManual);
+ assertTrue(globalInterceptors.get(2).getClass().toString(), globalInterceptors.get(2) instanceof MyTestInterceptorTwo);
+
+ // Try to register again (should have no effect
+ svc.registerInterceptor(myInterceptorManual);
+ globalInterceptors = svc.getGlobalInterceptorsForUnitTest();
+ assertEquals(3, globalInterceptors.size());
+ assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
+ assertTrue(globalInterceptors.get(1).getClass().toString(), globalInterceptors.get(1) instanceof MyTestInterceptorManual);
+ assertTrue(globalInterceptors.get(2).getClass().toString(), globalInterceptors.get(2) instanceof MyTestInterceptorTwo);
+
+ // Make sure we have the right invokers in the right order
+ List invokers = svc.getInterceptorsWithInvokersForPointcut(Pointcut.TEST_RB);
+ assertSame(interceptor0, invokers.get(0));
+ assertSame(myInterceptorManual, invokers.get(1));
+ assertSame(interceptor1, invokers.get(2));
+
+ // Finally, unregister it
+ svc.unregisterInterceptor(myInterceptorManual);
+ globalInterceptors = svc.getGlobalInterceptorsForUnitTest();
+ assertEquals(2, globalInterceptors.size());
+ assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
+ assertTrue(globalInterceptors.get(1).getClass().toString(), globalInterceptors.get(1) instanceof MyTestInterceptorTwo);
+
+ }
+
+ @Test
+ public void testInvokeGlobalInterceptorMethods() {
+ InterceptorService svc = new InterceptorService();
+
+ // Registered in opposite order to verify that the order on the annotation is used
+ MyTestInterceptorTwo interceptor1 = new MyTestInterceptorTwo();
+ MyTestInterceptorOne interceptor0 = new MyTestInterceptorOne();
+ svc.registerInterceptor(interceptor1);
+ svc.registerInterceptor(interceptor0);
+
+ boolean outcome = svc.callHooks(Pointcut.TEST_RB, new HookParams("A", "B"));
+ assertTrue(outcome);
+
+ assertThat(myInvocations, contains("MyTestInterceptorOne.testRb", "MyTestInterceptorTwo.testRb"));
+ assertSame("A", interceptor0.myLastString0);
+ assertSame("A", interceptor1.myLastString0);
+ assertSame("B", interceptor1.myLastString1);
+ }
+
+ @Test
+ public void testInvokeUsingSupplierArg() {
+ InterceptorService svc = new InterceptorService();
+
+ MyTestInterceptorOne interceptor0 = new MyTestInterceptorOne();
+ MyTestInterceptorTwo interceptor1 = new MyTestInterceptorTwo();
+ svc.registerInterceptor(interceptor0);
+ svc.registerInterceptor(interceptor1);
+
+ boolean outcome = svc.callHooks(Pointcut.TEST_RB, new HookParams("A", "B"));
+ assertTrue(outcome);
+
+ assertThat(myInvocations, contains("MyTestInterceptorOne.testRb", "MyTestInterceptorTwo.testRb"));
+ assertSame("A", interceptor0.myLastString0);
+ assertSame("A", interceptor1.myLastString0);
+ assertSame("B", interceptor1.myLastString1);
+ }
+
+ @Test
+ public void testInvokeGlobalInterceptorMethods_MethodAbortsProcessing() {
+ InterceptorService svc = new InterceptorService();
+
+ MyTestInterceptorOne interceptor0 = new MyTestInterceptorOne();
+ MyTestInterceptorTwo interceptor1 = new MyTestInterceptorTwo();
+ svc.registerInterceptor(interceptor0);
+ svc.registerInterceptor(interceptor1);
+
+ interceptor0.myNextReturn = false;
+
+ boolean outcome = svc.callHooks(Pointcut.TEST_RB, new HookParams("A", "B"));
+ assertFalse(outcome);
+
+ assertThat(myInvocations, contains("MyTestInterceptorOne.testRb"));
+ assertSame("A", interceptor0.myLastString0);
+ assertSame(null, interceptor1.myLastString0);
+ assertSame(null, interceptor1.myLastString1);
+ }
+
+ @Test
+ public void testCallHooksInvokedWithNullParameters() {
+ InterceptorService svc = new InterceptorService();
+
+ class NullParameterInterceptor {
+ private String myValue0 = "";
+ private String myValue1 = "";
+
+ @Hook(Pointcut.TEST_RB)
+ public void hook(String theValue0, String theValue1) {
+ myValue0 = theValue0;
+ myValue1 = theValue1;
+ }
+ }
+
+ NullParameterInterceptor interceptor;
+ HookParams params;
+
+ // Both null
+ interceptor = new NullParameterInterceptor();
+ svc.registerInterceptor(interceptor);
+ params = new HookParams()
+ .add(String.class, null)
+ .add(String.class, null);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ assertEquals(null, interceptor.myValue0);
+ assertEquals(null, interceptor.myValue1);
+ svc.unregisterAllInterceptors();
+
+ // First null
+ interceptor = new NullParameterInterceptor();
+ svc.registerInterceptor(interceptor);
+ params = new HookParams()
+ .add(String.class, null)
+ .add(String.class, "A");
+ svc.callHooks(Pointcut.TEST_RB, params);
+ assertEquals(null, interceptor.myValue0);
+ assertEquals("A", interceptor.myValue1);
+ svc.unregisterAllInterceptors();
+
+ // Second null
+ interceptor = new NullParameterInterceptor();
+ svc.registerInterceptor(interceptor);
+ params = new HookParams()
+ .add(String.class, "A")
+ .add(String.class, null);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ assertEquals("A", interceptor.myValue0);
+ assertEquals(null, interceptor.myValue1);
+ svc.unregisterAllInterceptors();
+
+ }
+
+ @Test
+ public void testCallHooksLogAndSwallowException() {
+ InterceptorService svc = new InterceptorService();
+
+ class LogAndSwallowInterceptor0 {
+ private boolean myHit;
+
+ @Hook(Pointcut.TEST_RB)
+ public void hook(String theValue0, String theValue1) {
+ myHit = true;
+ throw new IllegalStateException();
+ }
+ }
+ LogAndSwallowInterceptor0 interceptor0 = new LogAndSwallowInterceptor0();
+ svc.registerInterceptor(interceptor0);
+
+ class LogAndSwallowInterceptor1 {
+ private boolean myHit;
+
+ @Hook(Pointcut.TEST_RB)
+ public void hook(String theValue0, String theValue1) {
+ myHit = true;
+ throw new IllegalStateException();
+ }
+ }
+ LogAndSwallowInterceptor1 interceptor1 = new LogAndSwallowInterceptor1();
+ svc.registerInterceptor(interceptor1);
+
+ class LogAndSwallowInterceptor2 {
+ private boolean myHit;
+
+ @Hook(Pointcut.TEST_RB)
+ public void hook(String theValue0, String theValue1) {
+ myHit = true;
+ throw new NullPointerException("AAA");
+ }
+ }
+ LogAndSwallowInterceptor2 interceptor2 = new LogAndSwallowInterceptor2();
+ svc.registerInterceptor(interceptor2);
+
+ HookParams params = new HookParams()
+ .add(String.class, null)
+ .add(String.class, null);
+
+ try {
+ svc.callHooks(Pointcut.TEST_RB, params);
+ fail();
+ } catch (NullPointerException e) {
+ assertEquals("AAA", e.getMessage());
+ }
+
+ assertEquals(true, interceptor0.myHit);
+ assertEquals(true, interceptor1.myHit);
+ assertEquals(true, interceptor2.myHit);
+ }
+
+
+ @Test
+ public void testCallHooksInvokedWithWrongParameters() {
+ InterceptorService svc = new InterceptorService();
+
+ Integer msg = 123;
+ CanonicalSubscription subs = new CanonicalSubscription();
+ HookParams params = new HookParams(msg, subs);
+ try {
+ svc.callHooks(Pointcut.TEST_RB, params);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e.getMessage(), containsString("Invalid params for pointcut " + Pointcut.TEST_RB + " - Wanted java.lang.String,java.lang.String but found "));
+ }
+ }
+
+ @Test
+ public void testValidateParamTypes() {
+ InterceptorService svc = new InterceptorService();
+
+ HookParams params = new HookParams();
+ params.add(String.class, "A");
+ params.add(String.class, "B");
+ boolean validated = svc.haveAppropriateParams(Pointcut.TEST_RB, params);
+ assertTrue(validated);
+ }
+
+ @Test
+ public void testValidateParamTypesMissingParam() {
+ InterceptorService svc = new InterceptorService();
+
+ HookParams params = new HookParams();
+ params.add(String.class, "A");
+ try {
+ svc.haveAppropriateParams(Pointcut.TEST_RB, params);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Wrong number of params for pointcut " + Pointcut.TEST_RB + " - Wanted java.lang.String,java.lang.String but found [String]", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testValidateParamTypesExtraParam() {
+ InterceptorService svc = new InterceptorService();
+
+ HookParams params = new HookParams();
+ params.add(String.class, "A");
+ params.add(String.class, "B");
+ params.add(String.class, "C");
+ params.add(String.class, "D");
+ params.add(String.class, "E");
+ try {
+ svc.haveAppropriateParams(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, params);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Wrong number of params for pointcut " + Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED + " - Wanted ca.uhn.fhir.rest.api.server.RequestDetails,ca.uhn.fhir.rest.server.servlet.ServletRequestDetails,org.hl7.fhir.instance.model.api.IBaseResource,org.hl7.fhir.instance.model.api.IBaseResource but found [String, String, String, String, String]", e.getMessage());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testValidateParamTypesWrongParam() {
+ InterceptorService svc = new InterceptorService();
+
+ HookParams params = new HookParams();
+ params.add((Class) String.class, 1);
+ params.add((Class) String.class, 2);
+ params.add((Class) String.class, 3);
+ params.add((Class) String.class, 4);
+ try {
+ svc.haveAppropriateParams(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, params);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Invalid params for pointcut " + Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED + " - class java.lang.Integer is not of type class java.lang.String", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testThreadLocalHookInterceptor() {
+ InterceptorService svc = new InterceptorService();
+ svc.setThreadlocalInvokersEnabled(true);
+
+ HookParams params = new HookParams().add("A").add("B");
+
+ @Interceptor(order = 100)
+ class LocalInterceptor {
+
+ private int myCount = 0;
+
+ @Hook(Pointcut.TEST_RB)
+ public boolean testRb(String theString0, String theString1) {
+ myCount++;
+ return true;
+ }
+
+ }
+ LocalInterceptor interceptor = new LocalInterceptor();
+ svc.registerThreadLocalInterceptor(interceptor);
+ try {
+
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ assertEquals(5, interceptor.myCount);
+
+ } finally {
+ svc.unregisterThreadLocalInterceptor(interceptor);
+ }
+
+ // Call some more - The interceptor is removed so the count shouldn't change
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ assertEquals(5, interceptor.myCount);
+
+ }
+
+ /**
+ *
+ * JA 20190321 On my MBP 2018
+ * ThreadLocalEnabled=true - Performed 500000 loops in 8383.0ms - 0.017ms / loop
+ * ThreadLocalEnabled=false - Performed 500000 loops in 3743.0ms - 0.007ms / loop
+ * ThreadLocalEnabled=true - Performed 500000 loops in 6163.0ms - 0.012ms / loop
+ * ThreadLocalEnabled=false - Performed 500000 loops in 3487.0ms - 0.007ms / loop
+ * ThreadLocalEnabled=true - Performed 1000000 loops in 00:00:12.458 - 0.012ms / loop
+ * ThreadLocalEnabled=false - Performed 1000000 loops in 7046.0ms - 0.007ms / loop
+ *
+ */
+ @Test
+ @Ignore("Performance test - Not needed normally")
+ public void testThreadLocalHookInterceptorMicroBenchmark() {
+ threadLocalMicroBenchmark(true, 500000);
+ threadLocalMicroBenchmark(false, 500000);
+ threadLocalMicroBenchmark(true, 500000);
+ threadLocalMicroBenchmark(false, 500000);
+ threadLocalMicroBenchmark(true, 500000);
+ threadLocalMicroBenchmark(false, 500000);
+ }
+
+ private void threadLocalMicroBenchmark(boolean theThreadlocalInvokersEnabled, int theCount) {
+ InterceptorService svc = new InterceptorService();
+ svc.setThreadlocalInvokersEnabled(theThreadlocalInvokersEnabled);
+
+ HookParams params = new HookParams().add("A").add("B");
+
+ @Interceptor(order = 100)
+ class LocalInterceptor {
+
+ private int myCount = 0;
+
+ @Hook(Pointcut.TEST_RB)
+ public void testRb(String theString0, String theString1) {
+ myCount++;
+ }
+
+ }
+
+ LocalInterceptor interceptor = new LocalInterceptor();
+ StopWatch sw = new StopWatch();
+ for (int i = 0; i < theCount; i++) {
+
+ svc.registerThreadLocalInterceptor(interceptor);
+ try {
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ svc.callHooks(Pointcut.TEST_RB, params);
+ } finally {
+ svc.unregisterThreadLocalInterceptor(interceptor);
+ }
+
+ }
+
+ ourLog.info("ThreadLocalEnabled={} - Performed {} loops in {} - {} / loop - Outcomne: {}", theThreadlocalInvokersEnabled, theCount, sw.toString(), sw.formatMillisPerOperation(theCount), interceptor.myCount);
+ }
+
+ @Before
+ public void before() {
+ myInvocations.clear();
+ }
+
+ interface TestInterceptorWithAnnotationDefinedOnInterface_Interface {
+
+ @Hook(Pointcut.INTERCEPTOR_REGISTERED)
+ void registered();
+
+ }
+
+ @Interceptor(order = 100)
+ public class MyTestInterceptorOne {
+
+ private String myLastString0;
+ private boolean myNextReturn = true;
+
+ public MyTestInterceptorOne() {
+ super();
+ }
+
+ @Hook(Pointcut.TEST_RB)
+ public boolean testRb(String theString0) {
+ myLastString0 = theString0;
+ myInvocations.add("MyTestInterceptorOne.testRb");
+ return myNextReturn;
+ }
+
+ }
+
+ @Interceptor(order = 300)
+ public class MyTestInterceptorTwo {
+ private String myLastString0;
+ private String myLastString1;
+
+ @Hook(Pointcut.TEST_RB)
+ public boolean testRb(String theString0, String theString1) {
+ myLastString0 = theString0;
+ myLastString1 = theString1;
+ myInvocations.add("MyTestInterceptorTwo.testRb");
+ return true;
+ }
+ }
+
+ @Interceptor(order = 200)
+ public class MyTestInterceptorManual {
+ @Hook(Pointcut.TEST_RB)
+ public void testRb() {
+ myInvocations.add("MyTestInterceptorManual.testRb");
+ }
+ }
+
+ public static class TestInterceptorWithAnnotationDefinedOnInterface_Class implements TestInterceptorWithAnnotationDefinedOnInterface_Interface {
+
+ private int myRegisterCount = 0;
+
+ public int getRegisterCount() {
+ return myRegisterCount;
+ }
+
+ @Override
+ public void registered() {
+ myRegisterCount++;
+ }
+ }
+
+ /**
+ * Just a make-believe version of this class for the unit test
+ */
+ private static class CanonicalSubscription {
+ }
+
+ /**
+ * Just a make-believe version of this class for the unit test
+ */
+ private static class ResourceDeliveryMessage {
+ }
+
+ @Interceptor()
+ public static class InterceptorThatFailsOnRegister {
+
+ @Hook(Pointcut.INTERCEPTOR_REGISTERED)
+ public void start() throws Exception {
+ throw new Exception("InterceptorThatFailsOnRegister FAILED!");
+ }
+
+ }
+
+
+}
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java
index 1aaa20e5637..0d6fa51f749 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java
@@ -9,7 +9,8 @@ import java.util.Date;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
public class StopWatchTest {
@@ -99,7 +100,7 @@ public class StopWatchTest {
@Test
public void testFormatMillis() {
- assertEquals("0.134ms", StopWatch.formatMillis(0.1339d));
+ assertEquals("0.134ms", StopWatch.formatMillis(0.1339d).replace(',', '.'));
assertEquals("1000ms", StopWatch.formatMillis(DateUtils.MILLIS_PER_SECOND));
assertEquals("00:01:00.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_MINUTE));
assertEquals("00:01:01", StopWatch.formatMillis(DateUtils.MILLIS_PER_MINUTE + DateUtils.MILLIS_PER_SECOND));
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index 9f1d68a277a..7647ffcddb0 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -7,7 +7,6 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import org.apache.commons.lang3.time.DateUtils;
-import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -59,8 +58,9 @@ public class FhirServerConfig extends BaseJavaConfigDstu2 {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
- public IServerInterceptor loggingInterceptor() {
+ public LoggingInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java
index 25f47534628..41c8323d249 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu3.java
@@ -8,12 +8,9 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import org.apache.commons.lang3.time.DateUtils;
-import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -73,8 +70,9 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
- public IServerInterceptor loggingInterceptor() {
+ public LoggingInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java
index 433be06e184..c1f6052f2b0 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigR4.java
@@ -64,8 +64,9 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
- public IServerInterceptor loggingInterceptor() {
+ public LoggingInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
index c2bff0b1cfd..ef2ca8bd5d1 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java
@@ -13,6 +13,7 @@ import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
+import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
@@ -66,8 +67,8 @@ public class JpaServerDemo extends RestfulServer {
throw new IllegalStateException();
}
- List beans = myAppCtx.getBean(resourceProviderBeanName, List.class);
- setResourceProviders(beans);
+ ResourceProviderFactory beans = myAppCtx.getBean(resourceProviderBeanName, ResourceProviderFactory.class);
+ registerProviders(beans.createProviders());
/*
* The system provider implements non-resource-type methods, such as
@@ -85,7 +86,7 @@ public class JpaServerDemo extends RestfulServer {
} else {
throw new IllegalStateException();
}
- setPlainProviders(systemProvider);
+ registerProvider(systemProvider);
/*
* The conformance provider exports the supported resources, search parameters, etc for
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
index 4c94572bc23..582bda3a547 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java
@@ -21,6 +21,10 @@ package ca.uhn.fhir.rest.client.impl;
*/
import ca.uhn.fhir.context.*;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.*;
@@ -43,7 +47,11 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*;
-import java.io.*;
+import javax.annotation.Nonnull;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -65,13 +73,13 @@ public abstract class BaseClient implements IRestfulClient {
private final String myUrlBase;
private boolean myDontValidateConformance;
private EncodingEnum myEncoding = null; // default unspecified (will be XML)
- private List myInterceptors = new ArrayList();
private boolean myKeepResponses = false;
private IHttpResponse myLastResponse;
private String myLastResponseBody;
private Boolean myPrettyPrint = false;
private SummaryEnum mySummary;
private RequestFormatParamStyleEnum myRequestFormatParamStyle = RequestFormatParamStyleEnum.SHORT;
+ private IInterceptorService myInterceptorService;
BaseClient(IHttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) {
super();
@@ -92,6 +100,18 @@ public abstract class BaseClient implements IRestfulClient {
myEncoding = EncodingEnum.JSON;
}
+ setInterceptorService(new InterceptorService());
+ }
+
+ @Override
+ public IInterceptorService getInterceptorService() {
+ return myInterceptorService;
+ }
+
+ @Override
+ public void setInterceptorService(@Nonnull IInterceptorService theInterceptorService) {
+ Validate.notNull(theInterceptorService, "theInterceptorService must not be null");
+ myInterceptorService = theInterceptorService;
}
protected Map> createExtraParams(String theCustomAcceptHeader) {
@@ -149,14 +169,6 @@ public abstract class BaseClient implements IRestfulClient {
return myClient;
}
- /**
- * {@inheritDoc}
- */
- @Override
- public List getInterceptors() {
- return Collections.unmodifiableList(myInterceptors);
- }
-
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
@@ -276,15 +288,16 @@ public abstract class BaseClient implements IRestfulClient {
}
}
- for (IClientInterceptor nextInterceptor : myInterceptors) {
- nextInterceptor.interceptRequest(httpRequest);
- }
+ HookParams requestParams = new HookParams();
+ requestParams.add(IHttpRequest.class, httpRequest);
+ getInterceptorService().callHooks(Pointcut.CLIENT_REQUEST, requestParams);
response = httpRequest.execute();
- for (IClientInterceptor nextInterceptor : myInterceptors) {
- nextInterceptor.interceptResponse(response);
- }
+ HookParams responseParams = new HookParams();
+ responseParams.add(IHttpRequest.class, httpRequest);
+ responseParams.add(IHttpResponse.class, response);
+ getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams);
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) {
@@ -446,7 +459,7 @@ public abstract class BaseClient implements IRestfulClient {
@Override
public void registerInterceptor(IClientInterceptor theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null");
- myInterceptors.add(theInterceptor);
+ getInterceptorService().registerInterceptor(theInterceptor);
}
/**
@@ -461,7 +474,7 @@ public abstract class BaseClient implements IRestfulClient {
@Override
public void unregisterInterceptor(IClientInterceptor theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null");
- myInterceptors.remove(theInterceptor);
+ getInterceptorService().unregisterInterceptor(theInterceptor);
}
protected final class ResourceOrBinaryResponseHandler extends ResourceResponseHandler {
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java
index d6afdfc59e8..eae319a2b88 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/RestfulClientFactory.java
@@ -272,10 +272,9 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
@Override
public void validateServerBase(String theServerBase, IHttpClient theHttpClient, IRestfulClient theClient) {
GenericClient client = new GenericClient(myContext, theHttpClient, theServerBase, this);
+
+ client.setInterceptorService(theClient.getInterceptorService());
client.setEncoding(theClient.getEncoding());
- for (IClientInterceptor interceptor : theClient.getInterceptors()) {
- client.registerInterceptor(interceptor);
- }
client.setDontValidateConformance(true);
IBaseResource conformance;
diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java
index f847e27c23b..ec9d13b24cc 100644
--- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java
+++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java
@@ -156,10 +156,10 @@ public abstract class AbstractJaxRsBundleProvider extends AbstractJaxRsProvider
/**
* Default: an empty list of interceptors
*
- * @see ca.uhn.fhir.rest.server.IRestfulServer#getInterceptors()
+ * @see ca.uhn.fhir.rest.server.IRestfulServerDefaults#getInterceptors_()
*/
@Override
- public List getInterceptors() {
+ public List getInterceptors_() {
return Collections.emptyList();
}
diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java
index 82bb49faf26..ccd85e301be 100644
--- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java
+++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java
@@ -33,6 +33,7 @@ import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.LoggerFactory;
diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java
index 960d3914448..22edd41b12b 100644
--- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java
+++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java
@@ -25,6 +25,7 @@ import java.util.Map.Entry;
import javax.ws.rs.core.*;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.FhirContext;
@@ -74,6 +75,11 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
CTX = ctx;
}
+ @Override
+ public IInterceptorService getInterceptorService() {
+ return null;
+ }
+
/**
* DEFAULT = AddProfileTagEnum.NEVER
*/
@@ -145,10 +151,10 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
* Default: an empty list of interceptors (Interceptors are not yet supported
* in the JAX-RS server). Please get in touch if you'd like to help!
*
- * @see ca.uhn.fhir.rest.server.IRestfulServer#getInterceptors()
+ * @see ca.uhn.fhir.rest.server.IRestfulServerDefaults#getInterceptors_()
*/
@Override
- public List getInterceptors() {
+ public List getInterceptors_() {
return Collections.emptyList();
}
diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java
index 88567df696d..acd76ea72dd 100644
--- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java
+++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java
@@ -28,6 +28,7 @@ import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
@@ -102,7 +103,7 @@ implements IRestfulServer, IResourceProvider {
theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass);
}
- /**
+ /**
* The base for request for a resource provider has the following form:
* {@link AbstractJaxRsResourceProvider#getBaseForServer()
* getBaseForServer()} + "/" +
diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java
index 805042fa4dc..ad6534b4762 100644
--- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java
+++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java
@@ -67,6 +67,7 @@ public class JaxRsRequest extends RequestDetails {
*/
public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType,
RestOperationTypeEnum restOperation) {
+ super(server.getInterceptorService());
this.myHeaders = server.getHeaders();
this.myResourceString = resourceString;
this.setRestOperationType(restOperation);
diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java
index 32fa1bc439e..4b3841ce326 100644
--- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java
+++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsDummyPatientProviderDstu3.java
@@ -1,5 +1,6 @@
package ca.uhn.fhir.jaxrs.server.test;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import org.hl7.fhir.dstu3.model.Patient;
import ca.uhn.fhir.context.FhirContext;
@@ -18,4 +19,5 @@ public class TestJaxRsDummyPatientProviderDstu3 extends AbstractJaxRsResourcePro
public Class getResourceType() {
return Patient.class;
}
+
}
diff --git a/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProvider.java b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProvider.java
index a61c2a45b71..633fc4c72c2 100644
--- a/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProvider.java
+++ b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProvider.java
@@ -127,7 +127,7 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider getInterceptors() {
+ public List getInterceptors_() {
return Collections.emptyList();
}
diff --git a/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java
index 29d13bd2b44..4a132c14320 100644
--- a/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java
+++ b/hapi-fhir-jaxrsserver-example/src/main/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientRestProviderDstu3.java
@@ -128,7 +128,7 @@ public class JaxRsPatientRestProviderDstu3 extends AbstractJaxRsResourceProvider
/** THE DEFAULTS */
@Override
- public List getInterceptors() {
+ public List getInterceptors_() {
return Collections.emptyList();
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
index c99ce5df4f2..a8228f054ea 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
@@ -2,8 +2,8 @@ package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.HapiLocalizer;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.dao.DaoRegistry;
-import ca.uhn.fhir.jpa.model.interceptor.executor.InterceptorService;
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
@@ -16,6 +16,7 @@ import ca.uhn.fhir.jpa.subscription.module.cache.ISubscribableChannelFactory;
import ca.uhn.fhir.jpa.subscription.module.cache.LinkedBlockingQueueSubscribableChannelFactory;
import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
+import ca.uhn.fhir.jpa.util.JpaInterceptorService;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
@@ -177,15 +178,15 @@ public abstract class BaseConfig implements SchedulingConfigurer {
}
@Bean
- public InterceptorService interceptorRegistry() {
- return new InterceptorService("hapi-fhir-jpa");
+ public IInterceptorService jpaInterceptorService() {
+ return new JpaInterceptorService();
}
/**
* Subclasses may override
*/
protected boolean isSupported(String theResourceType) {
- return daoRegistry().getResourceDao(theResourceType) != null;
+ return daoRegistry().getResourceDaoIfExists(theResourceType) != null;
}
public static void configureEntityManagerFactory(LocalContainerEntityManagerFactoryBean theFactory, FhirContext theCtx) {
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 25b1479685e..ac82bd1e0a8 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
@@ -1,15 +1,15 @@
package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.context.*;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.dao.index.DaoSearchParamSynchronizer;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.dao.index.SearchParamWithInlineReferencesExtractor;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.entity.*;
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
@@ -99,9 +99,9 @@ import static org.apache.commons.lang3.StringUtils.*;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -131,8 +131,6 @@ public abstract class BaseHapiFhirDao implements IDao,
@Autowired
protected IdHelperService myIdHelperService;
@Autowired
- protected IInterceptorBroadcaster myInterceptorBroadcaster;
- @Autowired
protected IForcedIdDao myForcedIdDao;
@Autowired
protected ISearchResultDao mySearchResultDao;
@@ -193,6 +191,8 @@ public abstract class BaseHapiFhirDao implements IDao,
private SearchBuilderFactory mySearchBuilderFactory;
private ApplicationContext myApplicationContext;
+ @Autowired
+ protected IInterceptorBroadcaster myInterceptorBroadcaster;
/**
* Returns the newly created forced ID. If the entity already had a forced ID, or if
@@ -225,7 +225,7 @@ public abstract class BaseHapiFhirDao implements IDao,
}
if (theExpungeOptions.getLimit() < 1) {
- throw new InvalidRequestException("Expunge limit may not be less than 1. Received expunge limit "+theExpungeOptions.getLimit() + ".");
+ throw new InvalidRequestException("Expunge limit may not be less than 1. Received expunge limit " + theExpungeOptions.getLimit() + ".");
}
AtomicInteger remainingCount = new AtomicInteger(theExpungeOptions.getLimit());
@@ -624,10 +624,6 @@ public abstract class BaseHapiFhirDao implements IDao,
return myDaoRegistry.getResourceDaoIfExists(theType);
}
- protected IFhirResourceDao> getDaoOrThrowException(Class extends IBaseResource> theClass) {
- return myDaoRegistry.getDaoOrThrowException(theClass);
- }
-
protected TagDefinition getTagOrNull(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
if (isBlank(theScheme) && isBlank(theTerm) && isBlank(theLabel)) {
return null;
@@ -784,10 +780,6 @@ public abstract class BaseHapiFhirDao implements IDao,
if (theRequestDetails.getUserData().get(PROCESSING_SUB_REQUEST) == Boolean.TRUE) {
theRequestDetails.notifyIncomingRequestPreHandled(theOperationType);
}
- List interceptors = getConfig().getInterceptors();
- for (IServerInterceptor next : interceptors) {
- next.incomingRequestPreHandled(theOperationType, theRequestDetails);
- }
}
/**
@@ -1439,6 +1431,11 @@ public abstract class BaseHapiFhirDao implements IDao,
public ResourceTable updateInternal(RequestDetails theRequestDetails, T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion,
ResourceTable theEntity, IIdType theResourceId, IBaseResource theOldResource) {
+
+ // We'll update the resource ID with the correct version later but for
+ // now at least set it to something useful for the interceptors
+ theResource.setId(theEntity.getIdDt());
+
// Notify interceptors
ActionRequestDetails requestDetails;
if (theRequestDetails != null) {
@@ -1447,18 +1444,12 @@ public abstract class BaseHapiFhirDao implements IDao,
}
// Notify IServerOperationInterceptors about pre-action call
- if (theRequestDetails != null) {
- theRequestDetails.getRequestOperationCallback().resourcePreUpdate(theOldResource, theResource);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreUpdate(theRequestDetails, theOldResource, theResource);
- }
- }
HookParams hookParams = new HookParams()
.add(IBaseResource.class, theOldResource)
- .add(IBaseResource.class, theResource);
- myInterceptorBroadcaster.callHooks(Pointcut.OP_PRESTORAGE_RESOURCE_UPDATED, hookParams);
+ .add(IBaseResource.class, theResource)
+ .add(RequestDetails.class, theRequestDetails)
+ .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, hookParams);
// Perform update
ResourceTable savedEntity = updateEntity(theRequestDetails, theResource, theEntity, null, thePerformIndexing, thePerformIndexing, new Date(), theForceUpdateVersion, thePerformIndexing);
@@ -1480,23 +1471,15 @@ public abstract class BaseHapiFhirDao implements IDao,
// Notify interceptors
if (!savedEntity.isUnchangedInCurrentOperation()) {
- if (theRequestDetails != null) {
- theRequestDetails.getRequestOperationCallback().resourceUpdated(theResource);
- theRequestDetails.getRequestOperationCallback().resourceUpdated(theOldResource, theResource);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceUpdated(theRequestDetails, theResource);
- ((IServerOperationInterceptor) next).resourceUpdated(theRequestDetails, theOldResource, theResource);
- }
- }
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void beforeCommit(boolean readOnly) {
HookParams hookParams = new HookParams()
.add(IBaseResource.class, theOldResource)
- .add(IBaseResource.class, theResource);
- myInterceptorBroadcaster.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, hookParams);
+ .add(IBaseResource.class, theResource)
+ .add(RequestDetails.class, theRequestDetails)
+ .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, hookParams);
}
});
}
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 58e374f6a83..071816bb929 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
@@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -24,10 +24,10 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService;
import ca.uhn.fhir.jpa.model.entity.*;
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
@@ -52,6 +52,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
+import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.*;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*;
@@ -71,6 +72,7 @@ import javax.annotation.PostConstruct;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -218,14 +220,11 @@ public abstract class BaseHapiFhirResourceDao extends B
T resourceToDelete = toResource(myResourceType, entity, null, false);
// Notify IServerOperationInterceptors about pre-action call
- if (theRequest != null) {
- theRequest.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreDelete(theRequest, resourceToDelete);
- }
- }
+ HookParams hook = new HookParams()
+ .add(IBaseResource.class, resourceToDelete)
+ .add(RequestDetails.class, theRequest)
+ .addIfMatchesType(ServletRequestDetails.class, theRequest);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hook);
validateOkToDelete(theDeleteConflicts, entity, false);
@@ -242,25 +241,18 @@ public abstract class BaseHapiFhirResourceDao extends B
resourceToDelete.setId(entity.getIdDt());
// Notify JPA interceptors
- if (theRequest != null) {
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getContext(), theId.getResourceType(), theId);
- theRequest.getRequestOperationCallback().resourceDeleted(resourceToDelete);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceDeleted(theRequest, resourceToDelete);
- }
- }
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void beforeCommit(boolean readOnly) {
HookParams hookParams = new HookParams()
- .add(IBaseResource.class, resourceToDelete);
- myInterceptorBroadcaster.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_DELETED, hookParams);
+ .add(IBaseResource.class, resourceToDelete)
+ .add(RequestDetails.class, theRequest)
+ .addIfMatchesType(ServletRequestDetails.class, theRequest);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
}
});
- DaoMethodOutcome outcome = toMethodOutcome(savedEntity, resourceToDelete).setCreated(true);
+ DaoMethodOutcome outcome = toMethodOutcome(theRequest, savedEntity, resourceToDelete).setCreated(true);
IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(getContext());
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulDeletes", 1, w.getMillis());
@@ -308,14 +300,11 @@ public abstract class BaseHapiFhirResourceDao extends B
T resourceToDelete = toResource(myResourceType, entity, null, false);
// Notify IServerOperationInterceptors about pre-action call
- if (theRequest != null) {
- theRequest.getRequestOperationCallback().resourcePreDelete(resourceToDelete);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreDelete(theRequest, resourceToDelete);
- }
- }
+ HookParams hooks = new HookParams()
+ .add(IBaseResource.class, resourceToDelete)
+ .add(RequestDetails.class, theRequest)
+ .addIfMatchesType(ServletRequestDetails.class, theRequest);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, hooks);
validateOkToDelete(deleteConflicts, entity, false);
@@ -332,21 +321,14 @@ public abstract class BaseHapiFhirResourceDao extends B
resourceToDelete.setId(entity.getIdDt());
// Notify JPA interceptors
- if (theRequest != null) {
- theRequest.getRequestOperationCallback().resourceDeleted(resourceToDelete);
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, idToDelete.getResourceType(), idToDelete);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceDeleted(theRequest, resourceToDelete);
- }
- }
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void beforeCommit(boolean readOnly) {
HookParams hookParams = new HookParams()
- .add(IBaseResource.class, resourceToDelete);
- myInterceptorBroadcaster.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_DELETED, hookParams);
+ .add(IBaseResource.class, resourceToDelete)
+ .add(RequestDetails.class, theRequest)
+ .addIfMatchesType(ServletRequestDetails.class, theRequest);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
}
});
}
@@ -410,7 +392,7 @@ public abstract class BaseHapiFhirResourceDao extends B
entity = myEntityManager.find(ResourceTable.class, pid);
IBaseResource resource = toResource(entity, false);
theResource.setId(resource.getIdElement().getValue());
- return toMethodOutcome(entity, resource).setCreated(false);
+ return toMethodOutcome(theRequest, entity, resource).setCreated(false);
}
}
@@ -443,17 +425,11 @@ public abstract class BaseHapiFhirResourceDao extends B
}
// Notify JPA interceptors
- if (theRequest != null) {
- theRequest.getRequestOperationCallback().resourcePreCreate(theResource);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreCreate(theRequest, theResource);
- }
- }
HookParams hookParams = new HookParams()
- .add(IBaseResource.class, theResource);
- myInterceptorBroadcaster.callHooks(Pointcut.OP_PRESTORAGE_RESOURCE_CREATED, hookParams);
+ .add(IBaseResource.class, theResource)
+ .add(RequestDetails.class, theRequest)
+ .addIfMatchesType(ServletRequestDetails.class, theRequest);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, hookParams);
// Perform actual DB update
ResourceTable updatedEntity = updateEntity(theRequest, theResource, entity, null, thePerformIndexing, thePerformIndexing, theUpdateTime, false, thePerformIndexing);
@@ -488,25 +464,19 @@ public abstract class BaseHapiFhirResourceDao extends B
// Notify JPA interceptors
if (!updatedEntity.isUnchangedInCurrentOperation()) {
- if (theRequest != null) {
- theRequest.getRequestOperationCallback().resourceCreated(theResource);
- }
- for (IServerInterceptor next : getConfig().getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceCreated(theRequest, theResource);
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
+ @Override
+ public void beforeCommit(boolean readOnly) {
+ HookParams hookParams = new HookParams()
+ .add(IBaseResource.class, theResource)
+ .add(RequestDetails.class, theRequest)
+ .addIfMatchesType(ServletRequestDetails.class, theRequest);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, hookParams);
}
- }
+ });
}
- TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
- @Override
- public void beforeCommit(boolean readOnly) {
- HookParams hookParams = new HookParams()
- .add(IBaseResource.class, theResource);
- myInterceptorBroadcaster.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, hookParams);
- }
- });
- DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
+ DaoMethodOutcome outcome = toMethodOutcome(theRequest, entity, theResource).setCreated(true);
if (!thePerformIndexing) {
outcome.setId(theResource.getIdElement());
}
@@ -947,8 +917,11 @@ public abstract class BaseHapiFhirResourceDao extends B
}
// Interceptor broadcast: RESOURCE_MAY_BE_RETURNED
- HookParams params = new HookParams().add(IBaseResource.class, retVal);
- myInterceptorBroadcaster.callHooks(Pointcut.RESOURCE_MAY_BE_RETURNED, params);
+ HookParams params = new HookParams()
+ .add(IBaseResource.class, retVal)
+ .add(RequestDetails.class, theRequestDetails)
+ .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCE, params);
ourLog.debug("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return retVal;
@@ -1140,9 +1113,12 @@ public abstract class BaseHapiFhirResourceDao extends B
String uuid = UUID.randomUUID().toString();
SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(uuid);
- Iterator iter = builder.createQuery(theParams, searchRuntimeDetails);
- while (iter.hasNext()) {
- retVal.add(iter.next());
+ try (IResultIterator iter = builder.createQuery(theParams, searchRuntimeDetails)) {
+ while (iter.hasNext()) {
+ retVal.add(iter.next());
+ }
+ } catch (IOException e) {
+ ourLog.error("IO failure during database access", e);
}
return retVal;
@@ -1183,7 +1159,7 @@ public abstract class BaseHapiFhirResourceDao extends B
return retVal;
}
- private DaoMethodOutcome toMethodOutcome(@Nonnull final ResourceTable theEntity, @Nonnull IBaseResource theResource) {
+ private DaoMethodOutcome toMethodOutcome(RequestDetails theRequest, @Nonnull final ResourceTable theEntity, @Nonnull IBaseResource theResource) {
DaoMethodOutcome outcome = new DaoMethodOutcome();
IIdType id = null;
@@ -1201,9 +1177,12 @@ public abstract class BaseHapiFhirResourceDao extends B
outcome.setResource(theResource);
outcome.setEntity(theEntity);
- // Interceptor broadcast: RESOURCE_MAY_BE_RETURNED
- HookParams params = new HookParams().add(IBaseResource.class, theResource);
- myInterceptorBroadcaster.callHooks(Pointcut.RESOURCE_MAY_BE_RETURNED, params);
+ // Interceptor broadcast
+ HookParams params = new HookParams()
+ .add(IBaseResource.class, theResource)
+ .add(RequestDetails.class, theRequest)
+ .addIfMatchesType(ServletRequestDetails.class, theRequest);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCE, params);
return outcome;
}
@@ -1347,7 +1326,7 @@ public abstract class BaseHapiFhirResourceDao extends B
*/
if (!thePerformIndexing) {
theResource.setId(entity.getIdDt().getValue());
- DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(false);
+ DaoMethodOutcome outcome = toMethodOutcome(theRequestDetails, entity, theResource).setCreated(false);
outcome.setPreviousResource(oldResource);
return outcome;
}
@@ -1356,7 +1335,7 @@ public abstract class BaseHapiFhirResourceDao extends B
* Otherwise, we're not in a transaction
*/
ResourceTable savedEntity = updateInternal(theRequestDetails, theResource, thePerformIndexing, theForceUpdateVersion, entity, resourceId, oldResource);
- DaoMethodOutcome outcome = toMethodOutcome(savedEntity, theResource).setCreated(false);
+ DaoMethodOutcome outcome = toMethodOutcome(theRequestDetails, savedEntity, theResource).setCreated(false);
if (!thePerformIndexing) {
outcome.setId(theResource.getIdElement());
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 e0d55db4838..74638a9438c 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
@@ -114,7 +114,6 @@ public class DaoConfig {
* update setter javadoc if default changes
*/
private boolean myIndexContainedResources = true;
- private List myInterceptors = new ArrayList<>();
/**
* update setter javadoc if default changes
*/
@@ -527,47 +526,6 @@ public class DaoConfig {
myIndexMissingFieldsEnabled = theIndexMissingFields;
}
- /**
- * Returns the interceptors which will be notified of operations.
- *
- * @see #setInterceptors(List)
- * @deprecated Marked as deprecated as of HAPI 3.7.0. Use {@link #registerInterceptor} or {@link #unregisterInterceptor}instead.
- */
-
- @Deprecated
- public List getInterceptors() {
- return myInterceptors;
- }
-
- /**
- * This may be used to optionally register server interceptors directly against the DAOs.
- */
- public void setInterceptors(List theInterceptors) {
- myInterceptors = theInterceptors;
- }
-
- /**
- * This may be used to optionally register server interceptors directly against the DAOs.
- */
- public void setInterceptors(IServerInterceptor... theInterceptor) {
- setInterceptors(new ArrayList<>());
- if (theInterceptor != null && theInterceptor.length != 0) {
- getInterceptors().addAll(Arrays.asList(theInterceptor));
- }
- }
-
- public void registerInterceptor(IServerInterceptor theInterceptor) {
- Validate.notNull(theInterceptor, "Interceptor can not be null");
- if (!myInterceptors.contains(theInterceptor)) {
- myInterceptors.add(theInterceptor);
- }
- }
-
- public void unregisterInterceptor(IServerInterceptor theInterceptor) {
- Validate.notNull(theInterceptor, "Interceptor can not be null");
- myInterceptors.remove(theInterceptor);
- }
-
/**
* See {@link #setMaximumExpansionSize(int)}
*/
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java
index 313075c4d36..f9ec72a902b 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java
@@ -77,6 +77,9 @@ public class DaoRegistry implements ApplicationContextAware {
return retVal;
}
+ /**
+ * @throws InvalidRequestException If the given resource type is not supported
+ */
public IFhirResourceDao getResourceDao(String theResourceName) {
init();
IFhirResourceDao retVal = myResourceNameToResourceDao.get(theResourceName);
@@ -99,7 +102,19 @@ public class DaoRegistry implements ApplicationContextAware {
public IFhirResourceDao getResourceDaoIfExists(Class theResourceType) {
String resourceName = myContext.getResourceDefinition(theResourceType).getName();
- return (IFhirResourceDao) getResourceDao(resourceName);
+ try {
+ return (IFhirResourceDao) getResourceDao(resourceName);
+ } catch (InvalidRequestException e) {
+ return null;
+ }
+ }
+
+ public IFhirResourceDao getResourceDaoIfExists(String theResourceType) {
+ try {
+ return (IFhirResourceDao) getResourceDao(theResourceType);
+ } catch (InvalidRequestException e) {
+ return null;
+ }
}
private void init() {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java
index 2cfcf67dc86..3fbc2443c0d 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java
@@ -20,9 +20,10 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
+import java.io.Closeable;
import java.util.Iterator;
-public interface IResultIterator extends Iterator {
+public interface IResultIterator extends Iterator, Closeable {
int getSkippedCount();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
index 5a52b54919e..b9cc6446b99 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
@@ -29,9 +29,9 @@ import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService;
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
import ca.uhn.fhir.jpa.model.entity.*;
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
@@ -52,11 +52,13 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.*;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.annotations.VisibleForTesting;
@@ -76,6 +78,7 @@ import org.hibernate.query.criteria.internal.predicate.BooleanStaticAssertionPre
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.IdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -675,22 +678,14 @@ public class SearchBuilder implements ISearchBuilder {
value = value.substring(1);
}
- IdDt valueAsId = new IdDt(value);
+ IdType valueAsId = new IdType(value);
if (isNotBlank(value)) {
- if (valueAsId.isIdPartValidLong()) {
- orPids.add(valueAsId.getIdPartAsLong());
- } else {
- try {
- BaseHasResource entity = myCallingDao.readEntity(valueAsId);
- if (entity.getDeleted() == null) {
- orPids.add(entity.getId());
- }
- } catch (ResourceNotFoundException e) {
- /*
- * This isn't an error, just means no result found
- * that matches the ID the client provided
- */
- }
+ try {
+ Long pid = myIdHelperService.translateForcedIdToPid(myResourceName, valueAsId.getIdPart());
+ orPids.add(pid);
+ } catch (ResourceNotFoundException e) {
+ // This is not an error in a search, it just results in no matchesFhirResourceDaoR4InterceptorTest
+ ourLog.debug("Resource ID {} was requested but does not exist", valueAsId.getIdPart());
}
}
}
@@ -1892,8 +1887,11 @@ public class SearchBuilder implements ISearchBuilder {
}
// Interceptor broadcast: RESOURCE_MAY_BE_RETURNED
- HookParams params = new HookParams().add(IBaseResource.class, resource);
- myInterceptorBroadcaster.callHooks(Pointcut.RESOURCE_MAY_BE_RETURNED, params);
+ HookParams params = new HookParams()
+ .add(IBaseResource.class, resource)
+ .add(RequestDetails.class, null)
+ .add(ServletRequestDetails.class, null);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PREACCESS_RESOURCE, params);
theResourceListToPopulate.set(index, resource);
}
@@ -2489,7 +2487,7 @@ public class SearchBuilder implements ISearchBuilder {
private IncludesIterator myIncludesIterator;
private Long myNext;
private Iterator myPreResultsIterator;
- private Iterator myResultsIterator;
+ private ScrollableResultsIterator myResultsIterator;
private SortSpec mySort;
private boolean myStillNeedToFetchIncludes;
private int mySkipCount = 0;
@@ -2581,12 +2579,16 @@ public class SearchBuilder implements ISearchBuilder {
mySearchRuntimeDetails.setFoundMatchesCount(myPidSet.size());
if (myFirst) {
- myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED, mySearchRuntimeDetails);
+ HookParams params = new HookParams();
+ params.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, params);
myFirst = false;
}
if (NO_MORE.equals(myNext)) {
- myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE, mySearchRuntimeDetails);
+ HookParams params = new HookParams();
+ params.add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, params);
}
}
@@ -2613,6 +2615,12 @@ public class SearchBuilder implements ISearchBuilder {
return mySkipCount;
}
+ @Override
+ public void close() {
+ if (myResultsIterator != null) {
+ myResultsIterator.close();
+ }
+ }
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java
index 2fda9af8e20..008600cf064 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java
@@ -50,7 +50,6 @@ import com.google.common.collect.ArrayListMultimap;
import org.apache.commons.lang3.Validate;
import org.apache.http.NameValuePair;
import org.hibernate.Session;
-import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.internal.SessionImpl;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.exceptions.FHIRException;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java
index 739122096fe..634e45d22ec 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java
@@ -20,12 +20,13 @@ package ca.uhn.fhir.jpa.dao.index;
* #L%
*/
+import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
-import ca.uhn.fhir.jpa.model.search.PerformanceMessage;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import com.google.common.collect.ListMultimap;
@@ -35,6 +36,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import javax.annotation.Nonnull;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -52,6 +54,10 @@ public class IdHelperService {
myForcedIdDao.delete(forcedId);
}
+ /**
+ * @throws ResourceNotFoundException If the ID can not be found
+ */
+ @Nonnull
public Long translateForcedIdToPid(String theResourceName, String theResourceId) throws ResourceNotFoundException {
// We only pass 1 input in so only 0..1 will come back
IdDt id = new IdDt(theResourceName, theResourceId);
@@ -94,9 +100,11 @@ public class IdHelperService {
Collection nextIds = nextEntry.getValue();
if (isBlank(nextResourceType)) {
- PerformanceMessage msg = new PerformanceMessage()
+ StorageProcessingMessage msg = new StorageProcessingMessage()
.setMessage("This search uses unqualified resource IDs (an ID without a resource type). This is less efficient than using a qualified type.");
- theInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_MESSAGE, msg);
+ HookParams params = new HookParams()
+ .add(StorageProcessingMessage.class, msg);
+ theInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PROCESSING_MESSAGE, params);
retVal.addAll(theForcedIdDao.findByForcedId(nextIds));
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PerformanceTracingLoggingInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PerformanceTracingLoggingInterceptor.java
index c8631f10da4..0fff9798376 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PerformanceTracingLoggingInterceptor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PerformanceTracingLoggingInterceptor.java
@@ -20,16 +20,16 @@ package ca.uhn.fhir.jpa.interceptor;
* #L%
*/
-import ca.uhn.fhir.jpa.model.interceptor.api.Hook;
-import ca.uhn.fhir.jpa.model.interceptor.api.Interceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.Hook;
+import ca.uhn.fhir.interceptor.api.Interceptor;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.util.LogUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
-@Interceptor(manualRegistration = true)
+@Interceptor()
public class PerformanceTracingLoggingInterceptor {
private static final Logger ourLog = LoggerFactory.getLogger(PerformanceTracingLoggingInterceptor.class);
private final Logger myLog;
@@ -50,27 +50,27 @@ public class PerformanceTracingLoggingInterceptor {
myLevel = theLevel;
}
- @Hook(value = Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED)
+ @Hook(value = Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED)
public void searchFirstResultLoaded(SearchRuntimeDetails theOutcome) {
log("Initial query result returned in {} for query {}", theOutcome.getQueryStopwatch(), theOutcome.getSearchUuid());
}
- @Hook(value = Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE)
+ @Hook(value = Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE)
public void searchSelectComplete(SearchRuntimeDetails theOutcome) {
log("Query found {} matches in {} for query {}", theOutcome.getFoundMatchesCount(), theOutcome.getQueryStopwatch(), theOutcome.getSearchUuid());
}
- @Hook(value = Pointcut.PERFTRACE_SEARCH_COMPLETE)
+ @Hook(value = Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE)
public void searchComplete(SearchRuntimeDetails theOutcome) {
log("Query {} is complete in {} - Found {} matches", theOutcome.getSearchUuid(), theOutcome.getQueryStopwatch(), theOutcome.getFoundMatchesCount());
}
- @Hook(value = Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE)
+ @Hook(value = Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE)
public void searchPassComplete(SearchRuntimeDetails theOutcome) {
log("Query {} pass complete and set to status {} in {} - Found {} matches", theOutcome.getSearchUuid(), theOutcome.getSearchStatus(), theOutcome.getQueryStopwatch(), theOutcome.getFoundMatchesCount());
}
- @Hook(value = Pointcut.PERFTRACE_SEARCH_FAILED)
+ @Hook(value = Pointcut.JPA_PERFTRACE_SEARCH_FAILED)
public void searchFailed(SearchRuntimeDetails theOutcome) {
log("Query {} failed in {} - Found {} matches", theOutcome.getSearchUuid(), theOutcome.getQueryStopwatch(), theOutcome.getFoundMatchesCount());
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java
index 1fba4b84008..90365529583 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ServletSubRequestDetails.java
@@ -37,6 +37,7 @@ public class ServletSubRequestDetails extends ServletRequestDetails {
* @param theRequestDetails The parent request details
*/
public ServletSubRequestDetails(ServletRequestDetails theRequestDetails) {
+ super(theRequestDetails.getInterceptorBroadcaster());
if (theRequestDetails != null) {
Map> headers = theRequestDetails.getHeaders();
for (Map.Entry> next : headers.entrySet()) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
index 071aae70f87..d7b3584e2f2 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
@@ -21,14 +21,15 @@ package ca.uhn.fhir.jpa.search;
*/
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
@@ -72,6 +73,7 @@ import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
+import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
@@ -308,15 +310,19 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
// Load the results synchronously
final List pids = new ArrayList<>();
- Iterator resultIter = sb.createQuery(theParams, searchRuntimeDetails);
- while (resultIter.hasNext()) {
- pids.add(resultIter.next());
- if (loadSynchronousUpTo != null && pids.size() >= loadSynchronousUpTo) {
- break;
- }
- if (theParams.getLoadSynchronousUpTo() != null && pids.size() >= theParams.getLoadSynchronousUpTo()) {
- break;
+ try (IResultIterator resultIter = sb.createQuery(theParams, searchRuntimeDetails)) {
+ while (resultIter.hasNext()) {
+ pids.add(resultIter.next());
+ if (loadSynchronousUpTo != null && pids.size() >= loadSynchronousUpTo) {
+ break;
+ }
+ if (theParams.getLoadSynchronousUpTo() != null && pids.size() >= theParams.getLoadSynchronousUpTo()) {
+ break;
+ }
}
+ } catch (IOException e) {
+ ourLog.error("IO failure during database access", e);
+ throw new InternalErrorException(e);
}
/*
@@ -717,9 +723,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
mySearchRuntimeDetails.setSearchStatus(mySearch.getStatus());
if (mySearch.getStatus() == SearchStatusEnum.FINISHED) {
- myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_COMPLETE, mySearchRuntimeDetails);
+ HookParams params = new HookParams().add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE, params);
} else {
- myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE, mySearchRuntimeDetails);
+ HookParams params = new HookParams().add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE, params);
}
ourLog.info("Have completed search for [{}{}] and found {} resources in {}ms - Status is {}", mySearch.getResourceType(), mySearch.getSearchQueryString(), mySyncedPids.size(), sw.getMillis(), mySearch.getStatus());
@@ -760,7 +768,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
mySearch.setStatus(SearchStatusEnum.FAILED);
mySearchRuntimeDetails.setSearchStatus(mySearch.getStatus());
- myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_SEARCH_FAILED, mySearchRuntimeDetails);
+ HookParams params = new HookParams().add(SearchRuntimeDetails.class, mySearchRuntimeDetails);
+ myInterceptorBroadcaster.callHooks(Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params);
saveSearch();
@@ -891,51 +900,56 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
/*
* Construct the SQL query we'll be sending to the database
*/
- IResultIterator resultIterator = sb.createQuery(myParams, mySearchRuntimeDetails);
- assert (resultIterator != null);
+ try (IResultIterator resultIterator = sb.createQuery(myParams, mySearchRuntimeDetails)) {
+ assert (resultIterator != null);
- /*
- * The following loop actually loads the PIDs of the resources
- * matching the search off of the disk and into memory. After
- * every X results, we commit to the HFJ_SEARCH table.
- */
- int syncSize = mySyncSize;
- while (resultIterator.hasNext()) {
- myUnsyncedPids.add(resultIterator.next());
+ /*
+ * The following loop actually loads the PIDs of the resources
+ * matching the search off of the disk and into memory. After
+ * every X results, we commit to the HFJ_SEARCH table.
+ */
+ int syncSize = mySyncSize;
+ while (resultIterator.hasNext()) {
+ myUnsyncedPids.add(resultIterator.next());
- boolean shouldSync = myUnsyncedPids.size() >= syncSize;
+ boolean shouldSync = myUnsyncedPids.size() >= syncSize;
- if (myDaoConfig.getCountSearchResultsUpTo() != null &&
- myDaoConfig.getCountSearchResultsUpTo() > 0 &&
- myDaoConfig.getCountSearchResultsUpTo() < myUnsyncedPids.size()) {
- shouldSync = false;
- }
+ if (myDaoConfig.getCountSearchResultsUpTo() != null &&
+ myDaoConfig.getCountSearchResultsUpTo() > 0 &&
+ myDaoConfig.getCountSearchResultsUpTo() < myUnsyncedPids.size()) {
+ shouldSync = false;
+ }
+
+ if (myUnsyncedPids.size() > 50000) {
+ shouldSync = true;
+ }
+
+ // If no abort was requested, bail out
+ Validate.isTrue(isNotAborted(), "Abort has been requested");
+
+ if (shouldSync) {
+ saveUnsynced(resultIterator);
+ }
+
+ if (myLoadingThrottleForUnitTests != null) {
+ try {
+ Thread.sleep(myLoadingThrottleForUnitTests);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
- if (myUnsyncedPids.size() > 50000) {
- shouldSync = true;
}
// If no abort was requested, bail out
Validate.isTrue(isNotAborted(), "Abort has been requested");
- if (shouldSync) {
- saveUnsynced(resultIterator);
- }
-
- if (myLoadingThrottleForUnitTests != null) {
- try {
- Thread.sleep(myLoadingThrottleForUnitTests);
- } catch (InterruptedException e) {
- // ignore
- }
- }
+ saveUnsynced(resultIterator);
+ } catch (IOException e) {
+ ourLog.error("IO failure during database access", e);
+ throw new InternalErrorException(e);
}
-
- // If no abort was requested, bail out
- Validate.isTrue(isNotAborted(), "Abort has been requested");
-
- saveUnsynced(resultIterator);
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionActivatingInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionActivatingInterceptor.java
index 609106f268e..217bb5ffb5a 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionActivatingInterceptor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionActivatingInterceptor.java
@@ -26,9 +26,9 @@ import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.DaoRegistry;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
-import ca.uhn.fhir.jpa.model.interceptor.api.Hook;
-import ca.uhn.fhir.jpa.model.interceptor.api.Interceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.Hook;
+import ca.uhn.fhir.interceptor.api.Interceptor;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionCanonicalizer;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionConstants;
@@ -69,7 +69,7 @@ import java.util.concurrent.TimeUnit;
*/
@Service
@Lazy
-@Interceptor(manualRegistration = true)
+@Interceptor()
public class SubscriptionActivatingInterceptor {
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingInterceptor.class);
@@ -179,14 +179,14 @@ public class SubscriptionActivatingInterceptor {
submitResourceModified(theNewResource, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
}
- @Hook(Pointcut.OP_PRESTORAGE_RESOURCE_CREATED)
+ @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED)
public void addStrategyTagCreated(IBaseResource theResource) {
if (isSubscription(theResource)) {
validateCriteriaAndAddStrategy(theResource);
}
}
- @Hook(Pointcut.OP_PRESTORAGE_RESOURCE_UPDATED)
+ @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED)
public void addStrategyTagUpdated(IBaseResource theOldResource, IBaseResource theNewResource) {
if (isSubscription(theNewResource)) {
validateCriteriaAndAddStrategy(theNewResource);
@@ -204,17 +204,17 @@ public class SubscriptionActivatingInterceptor {
}
}
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED)
+ @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED)
public void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource) {
submitResourceModified(theNewResource, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
}
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED)
+ @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED)
public void resourceCreated(IBaseResource theResource) {
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE);
}
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_DELETED)
+ @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED)
public void resourceDeleted(IBaseResource theResource) {
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.DELETE);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionInterceptorLoader.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionInterceptorLoader.java
index 5af1faf0591..2a15dabf927 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionInterceptorLoader.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionInterceptorLoader.java
@@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.subscription;
*/
import ca.uhn.fhir.jpa.dao.DaoConfig;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
import com.google.common.annotations.VisibleForTesting;
@@ -47,9 +47,9 @@ public class SubscriptionInterceptorLoader {
@Autowired
private SubscriptionRegistry mySubscriptionRegistry;
@Autowired
- private ApplicationContext myAppicationContext;
+ private ApplicationContext myApplicationContext;
@Autowired
- private IInterceptorRegistry myInterceptorRegistry;
+ private IInterceptorService myInterceptorRegistry;
public void registerInterceptors() {
Set supportedSubscriptionTypes = myDaoConfig.getSupportedSubscriptionTypes();
@@ -69,7 +69,7 @@ public class SubscriptionInterceptorLoader {
private void loadSubscriptions() {
ourLog.info("Loading subscriptions into the SubscriptionRegistry...");
// Activate scheduled subscription loads into the SubscriptionRegistry
- myAppicationContext.getBean(SubscriptionLoader.class);
+ myApplicationContext.getBean(SubscriptionLoader.class);
ourLog.info("...{} subscriptions loaded", mySubscriptionRegistry.size());
}
@@ -77,5 +77,6 @@ public class SubscriptionInterceptorLoader {
void unregisterInterceptorsForUnitTest() {
myInterceptorRegistry.unregisterInterceptor(mySubscriptionActivatingInterceptor);
myInterceptorRegistry.unregisterInterceptor(mySubscriptionMatcherInterceptor);
+ mySubscriptionMatcherInterceptor.preDestroy();
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionMatcherInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionMatcherInterceptor.java
index a5a002954e8..2f375dfb70b 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionMatcherInterceptor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionMatcherInterceptor.java
@@ -1,10 +1,7 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.model.interceptor.api.Hook;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Interceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.*;
import ca.uhn.fhir.jpa.subscription.module.LinkedBlockingQueueSubscribableChannel;
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionChannelFactory;
@@ -33,9 +30,9 @@ import javax.annotation.PreDestroy;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -46,19 +43,19 @@ import javax.annotation.PreDestroy;
@Component
@Lazy
-@Interceptor(manualRegistration = true)
+@Interceptor()
public class SubscriptionMatcherInterceptor implements IResourceModifiedConsumer {
- private Logger ourLog = LoggerFactory.getLogger(SubscriptionMatcherInterceptor.class);
-
public static final String SUBSCRIPTION_MATCHING_CHANNEL_NAME = "subscription-matching";
protected SubscribableChannel myMatchingChannel;
-
+ @Autowired
+ protected SubscriptionChannelFactory mySubscriptionChannelFactory;
+ private Logger ourLog = LoggerFactory.getLogger(SubscriptionMatcherInterceptor.class);
@Autowired
private FhirContext myFhirContext;
@Autowired
private SubscriptionMatchingSubscriber mySubscriptionMatchingSubscriber;
@Autowired
- protected SubscriptionChannelFactory mySubscriptionChannelFactory;
+ private IInterceptorBroadcaster myInterceptorBroadcaster;
/**
* Constructor
@@ -85,28 +82,27 @@ public class SubscriptionMatcherInterceptor implements IResourceModifiedConsumer
}
}
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED)
+ @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED)
public void resourceCreated(IBaseResource theResource) {
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE);
}
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_DELETED)
+ @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED)
public void resourceDeleted(IBaseResource theResource) {
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.DELETE);
}
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED)
+ @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED)
public void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource) {
submitResourceModified(theNewResource, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
}
- @Autowired
- private IInterceptorBroadcaster myInterceptorBroadcaster;
-
private void submitResourceModified(IBaseResource theNewResource, ResourceModifiedMessage.OperationTypeEnum theOperationType) {
ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theNewResource, theOperationType);
// Interceptor call: SUBSCRIPTION_RESOURCE_MODIFIED
- if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, msg)) {
+ HookParams params = new HookParams()
+ .add(ResourceModifiedMessage.class, msg);
+ if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, params)) {
return;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java
index b05511d91da..9aac497f098 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java
@@ -1399,10 +1399,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery;
hibernateQuery.setFetchSize(myFetchSize);
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
- Iterator scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults);
+ try (ScrollableResultsIterator scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults)) {
+
+ while (scrollableResultsIterator.hasNext()) {
+ targets.add(scrollableResultsIterator.next());
+ }
- while (scrollableResultsIterator.hasNext()) {
- targets.add(scrollableResultsIterator.next());
}
ourLastResultsFromTranslationCache = false; // For testing.
@@ -1484,27 +1486,29 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery;
hibernateQuery.setFetchSize(myFetchSize);
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
- Iterator scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults);
+ try (ScrollableResultsIterator scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults)) {
- while (scrollableResultsIterator.hasNext()) {
- TermConceptMapGroupElement nextElement = scrollableResultsIterator.next();
- nextElement.getConceptMapGroupElementTargets().size();
- myEntityManager.detach(nextElement);
+ while (scrollableResultsIterator.hasNext()) {
+ TermConceptMapGroupElement nextElement = scrollableResultsIterator.next();
+ nextElement.getConceptMapGroupElementTargets().size();
+ myEntityManager.detach(nextElement);
- if (isNotBlank(targetCode) && isNotBlank(targetCodeSystem)) {
- for (Iterator iter = nextElement.getConceptMapGroupElementTargets().iterator(); iter.hasNext(); ) {
- TermConceptMapGroupElementTarget next = iter.next();
- if (targetCodeSystem.equals(next.getSystem())) {
- if (targetCode.equals(next.getCode())) {
- continue;
+ if (isNotBlank(targetCode) && isNotBlank(targetCodeSystem)) {
+ for (Iterator iter = nextElement.getConceptMapGroupElementTargets().iterator(); iter.hasNext(); ) {
+ TermConceptMapGroupElementTarget next = iter.next();
+ if (targetCodeSystem.equals(next.getSystem())) {
+ if (targetCode.equals(next.getCode())) {
+ continue;
+ }
}
- }
- iter.remove();
+ iter.remove();
+ }
}
+
+ elements.add(nextElement);
}
- elements.add(nextElement);
}
ourLastResultsFromTranslationWithReverseCache = false; // For testing.
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java
index 2e754d5d9cf..f0b4379a43c 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java
@@ -28,6 +28,11 @@ public class JpaConstants {
*/
public static final String OPERATION_EXPUNGE = "$expunge";
+ /**
+ * Operation name for the $match operation
+ */
+ public static final String OPERATION_MATCH = "$match";
+
/**
* @deprecated Replace with {@link #OPERATION_EXPUNGE}
*/
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaInterceptorService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaInterceptorService.java
new file mode 100644
index 00000000000..5a96b748777
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaInterceptorService.java
@@ -0,0 +1,99 @@
+package ca.uhn.fhir.jpa.util;
+
+import ca.uhn.fhir.interceptor.api.*;
+import ca.uhn.fhir.interceptor.executor.InterceptorService;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * The JPA server has two interceptor services (aka two registries of interceptors). One that lives
+ * in the JPA module and is created via Spring, and one that lives in the RestfulServer. We do this
+ * so that interceptors can be registered at the JPA level via Spring (which is convenient for
+ * lots of reasons) and also via the RestfulServer (which is how other interceptors work outside the
+ * JPA context)
+ *
+ * This class is basically a composite broadcaster that broadcasts events to the internal registry but
+ * also to
+ *
+ */
+public class JpaInterceptorService implements IInterceptorService {
+
+ private IInterceptorService myInterceptorBroadcaster = new InterceptorService("hapi-fhir-jpa");
+
+ @Override
+ public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
+ if (!myInterceptorBroadcaster.callHooks(thePointcut, theParams)) {
+ return false;
+ }
+ RequestDetails requestDetails = theParams.get(RequestDetails.class);
+ if (requestDetails != null) {
+ requestDetails.getInterceptorBroadcaster().callHooks(thePointcut, theParams);
+ }
+ return true;
+ }
+
+ @Override
+ public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
+ Object retVal = myInterceptorBroadcaster.callHooksAndReturnObject(thePointcut, theParams);
+ if (retVal == null) {
+ RequestDetails requestDetails = theParams.get(RequestDetails.class);
+ if (requestDetails != null) {
+ retVal = requestDetails.getInterceptorBroadcaster().callHooksAndReturnObject(thePointcut, theParams);
+ }
+ }
+ return retVal;
+ }
+
+ @Override
+ public boolean registerThreadLocalInterceptor(Object theInterceptor) {
+ return myInterceptorBroadcaster.registerThreadLocalInterceptor(theInterceptor);
+ }
+
+ @Override
+ public void unregisterThreadLocalInterceptor(Object theInterceptor) {
+ myInterceptorBroadcaster.unregisterThreadLocalInterceptor(theInterceptor);
+ }
+
+ @Override
+ public boolean registerInterceptor(Object theInterceptor) {
+ return myInterceptorBroadcaster.registerInterceptor(theInterceptor);
+ }
+
+ @Override
+ public void unregisterInterceptor(Object theInterceptor) {
+ myInterceptorBroadcaster.unregisterInterceptor(theInterceptor);
+ }
+
+ @Override
+ public void registerAnonymousInterceptor(Pointcut thePointcut, IAnonymousInterceptor theInterceptor) {
+ myInterceptorBroadcaster.registerAnonymousInterceptor(thePointcut, theInterceptor);
+ }
+
+ @Override
+ public void registerAnonymousInterceptor(Pointcut thePointcut, int theOrder, IAnonymousInterceptor theInterceptor) {
+ myInterceptorBroadcaster.registerAnonymousInterceptor(thePointcut, theOrder, theInterceptor);
+ }
+
+ @Override
+ public List getAllRegisteredInterceptors() {
+ return myInterceptorBroadcaster.getAllRegisteredInterceptors();
+ }
+
+ @Override
+ public void unregisterAllInterceptors() {
+ myInterceptorBroadcaster.unregisterAllInterceptors();
+ }
+
+ @Override
+ public void unregisterInterceptors(@Nullable Collection> theInterceptors) {
+ myInterceptorBroadcaster.unregisterInterceptors(theInterceptors);
+ }
+
+ @Override
+ public void registerInterceptors(@Nullable Collection> theInterceptors) {
+ myInterceptorBroadcaster.registerInterceptors(theInterceptors);
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ResourceProviderFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ResourceProviderFactory.java
new file mode 100644
index 00000000000..71d12b42c0f
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ResourceProviderFactory.java
@@ -0,0 +1,47 @@
+package ca.uhn.fhir.jpa.util;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * Copyright (C) 2014 - 2019 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import javax.annotation.Nonnull;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+public class ResourceProviderFactory {
+
+ private List> mySuppliers = new ArrayList<>();
+
+ public void addSupplier(@Nonnull Supplier theSupplier) {
+ mySuppliers.add(theSupplier);
+ }
+
+ public List createProviders() {
+ List retVal = new ArrayList<>();
+ for (Supplier next : mySuppliers) {
+ Object nextRp = next.get();
+ if (nextRp != null) {
+ retVal.add(nextRp);
+ }
+ }
+ return retVal;
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java
index ee505924157..31575f86c4c 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java
@@ -23,9 +23,10 @@ package ca.uhn.fhir.jpa.util;
import org.apache.commons.lang3.Validate;
import org.hibernate.ScrollableResults;
+import java.io.Closeable;
import java.util.Iterator;
-public class ScrollableResultsIterator extends BaseIterator implements Iterator {
+public class ScrollableResultsIterator extends BaseIterator implements Iterator, Closeable {
private boolean hasNext;
private T myNext;
private ScrollableResults myScroll;
@@ -60,4 +61,14 @@ public class ScrollableResultsIterator extends BaseIterator
myNext = null;
return next;
}
+
+
+ @Override
+ public void close() {
+ if (myScroll != null) {
+ myScroll.close();
+ myScroll = null;
+ }
+ }
+
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
index 597bbd13da6..4b3c149e13e 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
@@ -139,7 +139,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect");
extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
- extraProperties.put("hibernate.search.default.directory_provider", "ram");
+ extraProperties.put("hibernate.search.default.directory_provider", "local-heap");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
return extraProperties;
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java
index 178d2de710b..71eed8f1c4e 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3Config.java
@@ -92,7 +92,6 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
* starvation
*/
int maxThreads = (int) (Math.random() * 6.0) + 1;
- maxThreads = 1;
retVal.setMaxTotal(maxThreads);
return retVal;
@@ -139,7 +138,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect");
extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
- extraProperties.put("hibernate.search.default.directory_provider", "ram");
+ extraProperties.put("hibernate.search.default.directory_provider", "local-heap");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
extraProperties.put("hibernate.search.autoregister_listeners", "true");
return extraProperties;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java
index 42a84d3862b..89c8cdb6555 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java
@@ -135,7 +135,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect");
extraProperties.put("hibernate.search.model_mapping", ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory.class.getName());
- extraProperties.put("hibernate.search.default.directory_provider", "ram");
+ extraProperties.put("hibernate.search.default.directory_provider", "local-heap");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
extraProperties.put("hibernate.search.autoregister_listeners", "true");
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
index cb81f0d3af5..6c147a16bc0 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
@@ -1,10 +1,10 @@
package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.jpa.entity.TermConcept;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
@@ -12,6 +12,7 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
+import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.ExpungeOptions;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.jpa.util.LoggingRule;
@@ -19,9 +20,7 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
-import ca.uhn.fhir.rest.api.server.IRequestOperationCallback;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
-import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.StopWatch;
@@ -63,7 +62,6 @@ import static ca.uhn.fhir.util.TestUtil.randomizeLocale;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public abstract class BaseJpaTest {
@@ -85,12 +83,11 @@ public abstract class BaseJpaTest {
public LoggingRule myLoggingRule = new LoggingRule();
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected ServletRequestDetails mySrd;
- protected ArrayList myServerInterceptorList;
- protected IRequestOperationCallback myRequestOperationCallback = mock(IRequestOperationCallback.class);
+ protected InterceptorService myRequestOperationCallback;
@Autowired
protected DatabaseBackedPagingProvider myDatabaseBackedPagingProvider;
@Autowired
- protected IInterceptorRegistry myInterceptorRegistry;
+ protected IInterceptorService myInterceptorRegistry;
@Autowired
protected CircularQueueCaptureQueriesListener myCaptureQueriesListener;
@@ -131,11 +128,11 @@ public abstract class BaseJpaTest {
@Before
public void beforeInitMocks() {
+ myRequestOperationCallback = new InterceptorService();
+
MockitoAnnotations.initMocks(this);
- when(mySrd.getRequestOperationCallback()).thenReturn(myRequestOperationCallback);
- myServerInterceptorList = new ArrayList<>();
- when(mySrd.getServer().getInterceptors()).thenReturn(myServerInterceptorList);
+ when(mySrd.getInterceptorBroadcaster()).thenReturn(myRequestOperationCallback);
when(mySrd.getUserData()).thenReturn(new HashMap<>());
when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(new ArrayList<>());
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java
index f35f5e96492..44aa4b904da 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java
@@ -1,22 +1,20 @@
package ca.uhn.fhir.jpa.dao.dstu2;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.Enumeration;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.jpa.rp.dstu2.PatientResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
+import ca.uhn.fhir.util.TestUtil;
+import org.junit.AfterClass;
+import org.junit.Before;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
+import java.util.Enumeration;
-import org.junit.AfterClass;
-import org.junit.Before;
-
-import ca.uhn.fhir.jpa.rp.dstu2.PatientResourceProvider;
-import ca.uhn.fhir.rest.api.server.IRequestOperationCallback;
-import ca.uhn.fhir.rest.server.RestfulServer;
-import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
-import ca.uhn.fhir.util.TestUtil;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public abstract class BaseJpaDstu2SystemTest extends BaseJpaDstu2Test {
private RestfulServer myServer;
@@ -31,7 +29,7 @@ public abstract class BaseJpaDstu2SystemTest extends BaseJpaDstu2Test {
@Before
public void before() throws ServletException {
mySrd = mock(ServletRequestDetails.class);
- when(mySrd.getRequestOperationCallback()).thenReturn(mock(IRequestOperationCallback.class));
+ when(mySrd.getInterceptorBroadcaster()).thenReturn(mock(IInterceptorBroadcaster.class));
if (myServer == null) {
myServer = new RestfulServer(myFhirCtx);
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java
index 428bf928507..62740d2e324 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java
@@ -18,18 +18,19 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionLoader;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
+import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.*;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.EncodingEnum;
-import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.runner.RunWith;
@@ -39,6 +40,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
@@ -47,7 +49,6 @@ import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestDstu2Config.class})
@@ -104,7 +105,6 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Autowired
@Qualifier("myImmunizationDaoDstu2")
protected IFhirResourceDao myImmunizationDao;
- protected IServerInterceptor myInterceptor;
@Autowired
@Qualifier("myLocationDaoDstu2")
protected IFhirResourceDao myLocationDao;
@@ -145,7 +145,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
protected IFhirResourceDao myQuestionnaireResponseDao;
@Autowired
@Qualifier("myResourceProvidersDstu2")
- protected Object myResourceProviders;
+ protected ResourceProviderFactory myResourceProviders;
@Autowired
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
@Autowired(required = false)
@@ -183,12 +183,6 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Autowired
protected SubscriptionLoader mySubscriptionLoader;
- @Before
- public void beforeCreateInterceptor() {
- myInterceptor = mock(IServerInterceptor.class);
- myDaoConfig.setInterceptors(myInterceptor);
- }
-
@Before
public void beforeFlushFT() {
runInTransaction(() -> {
@@ -204,7 +198,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Before
@Transactional()
- public void beforePurgeDatabase() throws InterruptedException {
+ public void beforePurgeDatabase() {
purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry);
}
@@ -216,6 +210,11 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
}
+ @After
+ public void afterResetInterceptors() {
+ myInterceptorRegistry.unregisterAllInterceptors();
+ }
+
@Override
protected FhirContext getContext() {
return myFhirCtx;
@@ -239,7 +238,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Override
public TransactionTemplate newTxTemplate() {
TransactionTemplate retVal = new TransactionTemplate(myTxManager);
- retVal.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
+ retVal.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
retVal.afterPropertiesSet();
return retVal;
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2InterceptorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2InterceptorTest.java
deleted file mode 100644
index c9626785a91..00000000000
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2InterceptorTest.java
+++ /dev/null
@@ -1,424 +0,0 @@
-package ca.uhn.fhir.jpa.dao.dstu2;
-
-import ca.uhn.fhir.jpa.dao.DaoConfig;
-import ca.uhn.fhir.jpa.dao.DeleteMethodOutcome;
-import ca.uhn.fhir.model.dstu2.resource.Bundle;
-import ca.uhn.fhir.model.dstu2.resource.Patient;
-import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
-import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
-import ca.uhn.fhir.model.primitive.IdDt;
-import ca.uhn.fhir.rest.api.server.RequestDetails;
-import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
-import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
-import ca.uhn.fhir.util.TestUtil;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.instance.model.api.IIdType;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.*;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
-
-public class FhirResourceDaoDstu2InterceptorTest extends BaseJpaDstu2Test {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2InterceptorTest.class);
- private IServerOperationInterceptor myJpaInterceptor;
- private ServerOperationInterceptorAdapter myJpaInterceptorAdapter = new ServerOperationInterceptorAdapter();
-
- @After
- public void after() {
- myDaoConfig.getInterceptors().remove(myJpaInterceptor);
- myDaoConfig.getInterceptors().remove(myJpaInterceptorAdapter);
- myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
- }
-
- @Before
- public void before() {
- myJpaInterceptor = mock(IServerOperationInterceptor.class);
- myDaoConfig.getInterceptors().add(myJpaInterceptor);
- myDaoConfig.getInterceptors().add(myJpaInterceptorAdapter);
- }
-
-
- @Test
- public void testJpaCreate() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- ArgumentCaptor detailsCapt;
- ArgumentCaptor tableCapt;
-
- detailsCapt = ArgumentCaptor.forClass(RequestDetails.class);
- tableCapt = ArgumentCaptor.forClass(IBaseResource.class);
- verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
- assertNotNull(tableCapt.getValue().getIdElement().getIdPart());
- assertEquals(id, tableCapt.getValue().getIdElement().getIdPartAsLong());
-
- detailsCapt = ArgumentCaptor.forClass(RequestDetails.class);
- tableCapt = ArgumentCaptor.forClass(IBaseResource.class);
- verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
-
- /*
- * Not do a conditional create
- */
- p = new Patient();
- p.addName().addFamily("PATIENT1");
- Long id2 = myPatientDao.create(p, "Patient?family=PATIENT", mySrd).getId().getIdPartAsLong();
- assertEquals(id, id2);
-
- detailsCapt = ArgumentCaptor.forClass(RequestDetails.class);
- tableCapt = ArgumentCaptor.forClass(IBaseResource.class);
- verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
- verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
-
- }
-
- @Test
- public void testJpaDelete() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- myPatientDao.delete(new IdDt("Patient", id), mySrd);
-
- ArgumentCaptor detailsCapt;
- ArgumentCaptor tableCapt;
-
- detailsCapt = ArgumentCaptor.forClass(RequestDetails.class);
- tableCapt = ArgumentCaptor.forClass(IBaseResource.class);
- verify(myJpaInterceptor, times(1)).resourceDeleted(detailsCapt.capture(), tableCapt.capture());
- assertNotNull(tableCapt.getValue().getIdElement().getIdPart());
- assertEquals(id, tableCapt.getValue().getIdElement().getIdPartAsLong());
-
- }
-
- /*
- * *****************************************************
- * Note that non JPA specific operations get tested in individual
- * operation test methods too
- * *****************************************************
- */
-
- @Test
- public void testJpaUpdate() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- p = new Patient();
- p.setId(new IdDt(id));
- p.addName().addFamily("PATIENT1");
- Long id2 = myPatientDao.update(p, mySrd).getId().getIdPartAsLong();
- assertEquals(id, id2);
-
- ArgumentCaptor detailsCapt;
- ArgumentCaptor tableCapt;
-
- detailsCapt = ArgumentCaptor.forClass(RequestDetails.class);
- tableCapt = ArgumentCaptor.forClass(IBaseResource.class);
- verify(myJpaInterceptor, times(1)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
- assertNotNull(tableCapt.getValue().getIdElement().getIdPart());
- assertEquals(id, tableCapt.getValue().getIdElement().getIdPartAsLong());
-
- /*
- * Now do a conditional update
- */
-
- p = new Patient();
- p.setId(new IdDt(id));
- p.addName().addFamily("PATIENT2");
- id2 = myPatientDao.update(p, "Patient?family=PATIENT1", mySrd).getId().getIdPartAsLong();
- assertEquals(id, id2);
-
- detailsCapt = ArgumentCaptor.forClass(RequestDetails.class);
- tableCapt = ArgumentCaptor.forClass(IBaseResource.class);
- verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
- verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
- assertEquals(id, tableCapt.getAllValues().get(2).getIdElement().getIdPartAsLong());
-
- /*
- * Now do a conditional update where none will match (so this is actually a create)
- */
-
- p = new Patient();
- p.addName().addFamily("PATIENT3");
- id2 = myPatientDao.update(p, "Patient?family=ZZZ", mySrd).getId().getIdPartAsLong();
- assertNotEquals(id, id2);
-
- detailsCapt = ArgumentCaptor.forClass(RequestDetails.class);
- tableCapt = ArgumentCaptor.forClass(IBaseResource.class);
- verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
- verify(myJpaInterceptor, times(2)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
- assertEquals(id2, tableCapt.getAllValues().get(3).getIdElement().getIdPartAsLong());
-
-
- }
-
- @Test
- public void testRequestOperationCreate() {
- IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class);
- myServerInterceptorList.add(interceptor);
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
- Long id = res.getIdElement().getIdPartAsLong();
- assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
-
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- IIdType id = myPatientDao.create(p, mySrd).getId();
- assertEquals(1L, id.getVersionIdPartAsLong().longValue());
-
- verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
-
- @Test
- public void testRequestOperationDelete() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
- Long id = res.getIdElement().getIdPartAsLong();
- assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
-
- IIdType newId = myPatientDao.delete(new IdDt("Patient/" + id), mySrd).getId();
- assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
-
- verify(myRequestOperationCallback, times(1)).resourcePreDelete(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
-
-
- @Test
- public void testRequestOperationDeleteMulti() {
- myDaoConfig.setAllowMultipleDelete(true);
-
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id2 = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
- Long id = res.getIdElement().getIdPartAsLong();
- assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
-
- DeleteMethodOutcome outcome = myPatientDao.deleteByUrl("Patient?name=PATIENT", mySrd);
- String oo = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
- ourLog.info(oo);
- assertThat(oo, containsString("deleted 2 resource(s)"));
-
- verify(myRequestOperationCallback, times(2)).resourcePreDelete(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(2)).resourceDeleted(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(2)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
- @Test
- public void testRequestOperationTransactionCreate() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
- Long id = res.getIdElement().getIdPartAsLong();
- assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
-
- Bundle xactBundle = new Bundle();
- xactBundle.setType(BundleTypeEnum.TRANSACTION);
- xactBundle
- .addEntry()
- .setResource(p)
- .getRequest()
- .setUrl("Patient")
- .setMethod(HTTPVerbEnum.POST);
- Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
-
- IdDt newId = new IdDt(resp.getEntry().get(0).getResponse().getLocation());
- assertEquals(1L, newId.getVersionIdPartAsLong().longValue());
-
- verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
- @Test
- public void testRequestOperationTransactionDelete() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
- Long id = res.getIdElement().getIdPartAsLong();
- assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
-
- Bundle xactBundle = new Bundle();
- xactBundle.setType(BundleTypeEnum.TRANSACTION);
- xactBundle
- .addEntry()
- .getRequest()
- .setUrl("Patient/" + id)
- .setMethod(HTTPVerbEnum.DELETE);
- Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
-
- IdDt newId = new IdDt(resp.getEntry().get(0).getResponse().getLocation());
- assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
-
- verify(myRequestOperationCallback, times(1)).resourcePreDelete(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
- @Test
- public void testRequestOperationTransactionDeleteMulti() {
- myDaoConfig.setAllowMultipleDelete(true);
-
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- p = new Patient();
- p.addName().addFamily("PATIENT");
- Long id2 = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
- Long id = res.getIdElement().getIdPartAsLong();
- assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
-
- Bundle xactBundle = new Bundle();
- xactBundle.setType(BundleTypeEnum.TRANSACTION);
- xactBundle
- .addEntry()
- .getRequest()
- .setUrl("Patient?name=PATIENT")
- .setMethod(HTTPVerbEnum.DELETE);
- Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
- assertNotNull(resp);
-
- verify(myRequestOperationCallback, times(2)).resourcePreDelete(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(2)).resourceDeleted(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(2)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
- @Test
- public void testRequestOperationTransactionUpdate() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- p = new Patient();
- p.setId(new IdDt("Patient/" + id));
- p.addName().addFamily("PATIENT2");
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[1];
- assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
-
- Bundle xactBundle = new Bundle();
- xactBundle.setType(BundleTypeEnum.TRANSACTION);
- xactBundle
- .addEntry()
- .setResource(p)
- .getRequest()
- .setUrl("Patient/" + id)
- .setMethod(HTTPVerbEnum.PUT);
- Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
-
- IdDt newId = new IdDt(resp.getEntry().get(0).getResponse().getLocation());
- assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
-
- verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
- @Test
- public void testRequestOperationUpdate() {
- Patient p = new Patient();
- p.addName().addFamily("PATIENT");
- final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
-
- doAnswer(new Answer() {
- @Override
- public Void answer(InvocationOnMock theInvocation) throws Throwable {
- IBaseResource res = (IBaseResource) theInvocation.getArguments()[1];
- assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
- return null;
- }}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
-
- p = new Patient();
- p.setId(new IdDt("Patient/" + id));
- p.addName().addFamily("PATIENT2");
- IIdType newId = myPatientDao.update(p, mySrd).getId();
- assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
-
- verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class));
- verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
- verifyNoMoreInteractions(myRequestOperationCallback);
- }
-
- @AfterClass
- public static void afterClassClearContext() {
- TestUtil.clearAllStaticFieldsForUnitTest();
- }
-}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java
index 7fc9c56374b..da735603035 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java
@@ -152,15 +152,12 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
public void testSearchAndReindex() {
SearchParameterMap map;
- final IIdType pId1= newTxTemplate().execute(new TransactionCallback() {
- @Override
- public IIdType doInTransaction(TransactionStatus theStatus) {
- // TODO Auto-generated method stub
- Patient patient = new Patient();
- patient.getText().setDiv("
DIVAAA
");
- patient.addName().addGiven("NAMEAAA");
- return myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
- }
+ final IIdType pId1= newTxTemplate().execute(t -> {
+ // TODO Auto-generated method stub
+ Patient patient = new Patient();
+ patient.getText().setDiv("
DIVAAA
");
+ patient.addName().addGiven("NAMEAAA");
+ return myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
});
map = new SearchParameterMap();
@@ -179,7 +176,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
Patient patient = new Patient();
- patient.setId(pId1);
+ patient.setId(pId1.getValue());
patient.getText().setDiv("
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.
- *
- *
- *
- * Hooks may return void or may return a boolean. If the method returns
- * void or true, processing will continue normally. If the method
- * returns false, subscription processing will not proceed for the given resource;
- *
- */
- SUBSCRIPTION_RESOURCE_MODIFIED("ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
-
- /**
- * Invoked any time that a resource is matched by an individual subscription, and
- * is about to be queued for delivery.
- *
- * Hooks may make changes to the delivery payload, or make changes to the
- * canonical subscription such as adding headers, modifying the channel
- * endpoint, etc.
- *
- * Hooks may accept the following parameters:
- *
- * Hooks may return void or may return a boolean. If the method returns
- * void or true, processing will continue normally. If the method
- * returns false, delivery will be aborted.
- *
- */
- SUBSCRIPTION_RESOURCE_MATCHED("ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchResult"),
-
-
- /**
- * Invoked whenever a persisted resource was checked against all active subscriptions, and did not
- * match any.
- *
- * Hooks may accept the following parameters:
- *
- *
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks should not modify this parameter as changes will not have any effect.
- *
- *
- *
- * Hooks should return void.
- *
- */
- SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS("ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
-
-
- /**
- * Invoked immediately before the delivery of a subscription, and right before any channel-specific
- * hooks are invoked (e.g. {@link #SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY}.
- *
- * Hooks may make changes to the delivery payload, or make changes to the
- * canonical subscription such as adding headers, modifying the channel
- * endpoint, etc.
- *
- * Hooks may accept the following parameters:
- *
- * Hooks may return void or may return a boolean. If the method returns
- * void or true, processing will continue normally. If the method
- * returns false, processing will be aborted.
- *
- */
- SUBSCRIPTION_BEFORE_DELIVERY("ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
-
- /**
- * Invoked immediately after the delivery of a subscription, and right before any channel-specific
- * hooks are invoked (e.g. {@link #SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY}.
- *
- * Hooks may accept the following parameters:
- *
- */
- SUBSCRIPTION_AFTER_DELIVERY("ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
-
- /**
- * Invoked immediately after the attempted delivery of a subscription, if the delivery
- * failed.
- *
- * Hooks may accept the following parameters:
- *
- *
- *
java.lang.Exception - The exception that caused the failure. Note this could be an exception thrown by a SUBSCRIPTION_BEFORE_DELIVERY or SUBSCRIPTION_AFTER_DELIVERY interceptor
- *
ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage - the message that triggered the exception
- *
java.lang.Exception
- *
- *
- * Hooks may return void or may return a boolean. If the method returns
- * void or true, processing will continue normally, meaning that
- * an exception will be thrown by the delivery mechanism. This typically means that the
- * message will be returned to the processing queue. If the method
- * returns false, processing will be aborted and no further action will be
- * taken for the delivery.
- *
- */
- SUBSCRIPTION_AFTER_DELIVERY_FAILED("ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage", "java.lang.Exception"),
-
- /**
- * Invoked immediately after the delivery of a REST HOOK subscription.
- *
- * When this hook is called, all processing is complete so this hook should not
- * make any changes to the parameters.
- *
- * Hooks may accept the following parameters:
- *
- */
- SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY("ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
-
- /**
- * Invoked immediately before the delivery of a REST HOOK subscription.
- *
- * Hooks may make changes to the delivery payload, or make changes to the
- * canonical subscription such as adding headers, modifying the channel
- * endpoint, etc.
- *
- * Hooks may accept the following parameters:
- *
- * Hooks may return void or may return a boolean. If the method returns
- * void or true, processing will continue normally. If the method
- * returns false, processing will be aborted.
- *
- */
- SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY("ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage"),
-
- /**
- * Invoked whenever a persisted resource (a resource that has just been stored in the
- * database via a create/update/patch/etc.) is about to be checked for whether any subscriptions
- * were triggered as a result of the operation.
- *
- * Hooks may accept the following parameters:
- *
- *
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - Hooks may modify this parameter. This will affect the checking process.
- *
- *
- *
- * Hooks may return void or may return a boolean. If the method returns
- * void or true, processing will continue normally. If the method
- * returns false, processing will be aborted.
- *
- */
- SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED("ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
-
-
- /**
- * Invoked whenever a persisted resource (a resource that has just been stored in the
- * database via a create/update/patch/etc.) has been checked for whether any subscriptions
- * were triggered as a result of the operation.
- *
- * Hooks may accept the following parameters:
- *
- *
ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage - This parameter should not be modified as processing is complete when this hook is invoked.
- *
- *
- *
- * Hooks should return void.
- *
- */
- SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED("ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage"),
-
- /**
- * Invoked immediately after an active subscription is "registered". In HAPI FHIR, when
- * a subscription
- *
- * Hooks may make changes to the canonicalized subscription and this will have an effect
- * on processing across this server. Note however that timing issues may occur, since the
- * subscription is already technically live by the time this hook is called.
- *
- * Hooks may accept the following parameters:
- *
- */
- SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED("ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription"),
-
- /**
- * Invoked before a resource will be created, immediately before the resource
- * is persisted to the database.
- *
- * Hooks will have access to the contents of the resource being created
- * and may choose to make modifications to it. These changes will be
- * reflected in permanent storage.
- *
- * Hooks may accept the following parameters:
- *
- *
org.hl7.fhir.instance.model.api.IBaseResource
- *
- *
- * Hooks should return void.
- *
- */
- OP_PRESTORAGE_RESOURCE_CREATED("org.hl7.fhir.instance.model.api.IBaseResource"),
-
- /**
- * Invoked before a resource will be created, immediately before the transaction
- * is committed (after all validation and other business rules have successfully
- * completed, and any other database activity is complete.
- *
- * Hooks will have access to the contents of the resource being created
- * but should generally not make any
- * changes as storage has already occurred. Changes will not be reflected
- * in storage, but may be reflected in the HTTP response.
- *
- * Hooks may accept the following parameters:
- *
- *
org.hl7.fhir.instance.model.api.IBaseResource
- *
- *
- * Hooks should return void.
- *
- */
- OP_PRECOMMIT_RESOURCE_CREATED("org.hl7.fhir.instance.model.api.IBaseResource"),
-
- /**
- * Invoked before a resource will be created
- *
- * Hooks will have access to the contents of the resource being deleted
- * but should not make any changes as storage has already occurred
- *
- * Hooks may accept the following parameters:
- *
- *
org.hl7.fhir.instance.model.api.IBaseResource
- *
- *
- * Hooks should return void.
- *
- */
- OP_PRECOMMIT_RESOURCE_DELETED("org.hl7.fhir.instance.model.api.IBaseResource"),
-
- /**
- * Invoked before a resource will be updated, immediately before the transaction
- * is committed (after all validation and other business rules have successfully
- * completed, and any other database activity is complete.
- *
- * Hooks will have access to the contents of the resource being updated
- * (both the previous and new contents) but should generally not make any
- * changes as storage has already occurred. Changes will not be reflected
- * in storage, but may be reflected in the HTTP response.
- *
- * Hooks may accept the following parameters:
- *
org.hl7.fhir.instance.model.api.IBaseResource (new contents)
- *
- *
- * Hooks should return void.
- *
- */
- OP_PRECOMMIT_RESOURCE_UPDATED("org.hl7.fhir.instance.model.api.IBaseResource", "org.hl7.fhir.instance.model.api.IBaseResource"),
-
-
- /**
- * Invoked before a resource will be updated, immediately before the resource
- * is persisted to the database.
- *
- * Hooks will have access to the contents of the resource being updated
- * (both the previous and new contents) and may choose to make modifications
- * to the new contents of the resource. These changes will be reflected in
- * permanent storage.
- *
- * Hooks may accept the following parameters:
- *
org.hl7.fhir.instance.model.api.IBaseResource (new contents)
- *
- *
- * Hooks should return void.
- *
- */
- OP_PRESTORAGE_RESOURCE_UPDATED("org.hl7.fhir.instance.model.api.IBaseResource", "org.hl7.fhir.instance.model.api.IBaseResource"),
-
- /**
- * Invoked when a resource may be returned to the user, whether as a part of a READ,
- * a SEARCH, or even as the response to a CREATE/UPDATE, etc.
- *
- * This hook is invoked when a resource has been loaded by the storage engine and
- * is being returned to the HTTP stack for response. This is not a guarantee that the
- * client will ultimately see it, since filters/headers/etc may affect what
- * is returned but if a resource is loaded it is likely to be used.
- * Note also that caching may affect whether this pointcut is invoked.
- *
- *
- * Hooks will have access to the contents of the resource being returned
- * and may choose to make modifications. These changes will be reflected in
- * returned resource but have no effect on storage.
- *
- * Hooks may accept the following parameters:
- *
- *
org.hl7.fhir.instance.model.api.IBaseResource (the resource being returned)
- *
- *
- * Hooks should return void.
- *
- */
- RESOURCE_MAY_BE_RETURNED("org.hl7.fhir.instance.model.api.IBaseResource"),
-
- /**
- * Note that this is a performance tracing hook. Use with caution in production
- * systems, since calling it may (or may not) carry a cost.
- *
- * This hook is invoked when a search has returned the very first result
- * from the database. The timing on this call can be a good indicator of how
- * performant a query is in general.
- *
- * Hooks may accept the following parameters:
- *
- *
- * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
- * performed. Hooks should not modify this object.
- *
- *
- *
- * Hooks should return void.
- *
- */
- PERFTRACE_SEARCH_FIRST_RESULT_LOADED(SearchRuntimeDetails.class.getName()),
-
- /**
- * Note that this is a performance tracing hook. Use with caution in production
- * systems, since calling it may (or may not) carry a cost.
- *
- * This hook is invoked when an individual search query SQL SELECT statement
- * has completed and no more results are available from that query. Note that this
- * doesn't necessarily mean that no more matching results exist in the database,
- * since HAPI FHIR JPA batch loads results in to the query cache in chunks in order
- * to provide predicable results without overloading memory or the database.
- *
- * Hooks may accept the following parameters:
- *
- *
- * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
- * performed. Hooks should not modify this object.
- *
- *
- *
- * Hooks should return void.
- *
- */
- PERFTRACE_SEARCH_SELECT_COMPLETE(SearchRuntimeDetails.class.getName()),
-
- /**
- * Note that this is a performance tracing hook. Use with caution in production
- * systems, since calling it may (or may not) carry a cost.
- *
- * This hook is invoked when a search has failed for any reason. When this pointcut
- * is invoked, the search has completed unsuccessfully and will not be continued.
- *
- * Hooks may accept the following parameters:
- *
- *
- * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
- * performed. Hooks should not modify this object.
- *
- *
- *
- * Hooks should return void.
- *
- */
- PERFTRACE_SEARCH_FAILED(SearchRuntimeDetails.class.getName()),
-
- /**
- * Note that this is a performance tracing hook. Use with caution in production
- * systems, since calling it may (or may not) carry a cost.
- *
- * This hook is invoked when a search has failed for any reason. When this pointcut
- * is invoked, a pass in the Search Coordinator has completed successfully, but
- * not all possible resources have been loaded yet so a future paging request
- * may trigger a new task that will load further resources.
- *
- * Hooks may accept the following parameters:
- *
- *
- * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
- * performed. Hooks should not modify this object.
- *
- *
- *
- * Hooks should return void.
- *
- */
- PERFTRACE_SEARCH_PASS_COMPLETE(SearchRuntimeDetails.class.getName()),
-
- /**
- * Note that this is a performance tracing hook. Use with caution in production
- * systems, since calling it may (or may not) carry a cost.
- *
- * This hook is invoked when a search has failed for any reason. When this pointcut
- * is invoked, a pass in the Search Coordinator has completed successfully, and all
- * possible results have been fetched and loaded into the query cache.
- *
- * Hooks may accept the following parameters:
- *
- *
- * ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails - Contains details about the search being
- * performed. Hooks should not modify this object.
- *
- *
- *
- * Hooks should return void.
- *
- */
- PERFTRACE_SEARCH_COMPLETE(SearchRuntimeDetails.class.getName()),
-
-
- /**
- * Note that this is a performance tracing hook. Use with caution in production
- * systems, since calling it may (or may not) carry a cost.
- *
- * This hook is invoked when any informational or warning messages generated by the
- * SearchCoordinator are created. It is typically used to provide logging
- * or capture details related to a specific request.
- *
- * Hooks may accept the following parameters:
- *
- *
- * {@link PerformanceMessage} - Contains the message
- *
- *
- *
- * Hooks should return void.
- *
- */
- PERFTRACE_MESSAGE(PerformanceMessage.class.getName())
-
- ;
- private final List myParameterTypes;
-
- Pointcut(String... theParameterTypes) {
- myParameterTypes = Collections.unmodifiableList(Arrays.asList(theParameterTypes));
- }
-
- public List getParameterTypes() {
- return myParameterTypes;
- }
-}
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/PerformanceMessage.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/StorageProcessingMessage.java
similarity index 88%
rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/PerformanceMessage.java
rename to hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/StorageProcessingMessage.java
index 348da655860..b849948e168 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/PerformanceMessage.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/StorageProcessingMessage.java
@@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.model.search;
* #L%
*/
-public class PerformanceMessage {
+public class StorageProcessingMessage {
private String myMessage;
@@ -28,7 +28,7 @@ public class PerformanceMessage {
return myMessage;
}
- public PerformanceMessage setMessage(String theMessage) {
+ public StorageProcessingMessage setMessage(String theMessage) {
myMessage = theMessage;
return this;
}
diff --git a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorServiceTest.java b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorServiceTest.java
deleted file mode 100644
index 91d96071d46..00000000000
--- a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorServiceTest.java
+++ /dev/null
@@ -1,428 +0,0 @@
-package ca.uhn.fhir.jpa.model.interceptor.executor;
-
-import ca.uhn.fhir.jpa.model.interceptor.api.Hook;
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
-import ca.uhn.fhir.jpa.model.interceptor.api.Interceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
-import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
-import ca.uhn.fhir.util.StopWatch;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.r4.model.Patient;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.Order;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.hamcrest.Matchers.contains;
-import static org.junit.Assert.*;
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(classes = {InterceptorServiceTest.InterceptorRegistryTestCtxConfig.class})
-public class InterceptorServiceTest {
-
- private static final Logger ourLog = LoggerFactory.getLogger(InterceptorServiceTest.class);
- private static boolean ourNext_beforeRestHookDelivery_Return1;
- private static List ourInvocations = new ArrayList<>();
- private static IBaseResource ourLastResourceOne;
- private static IBaseResource ourLastResourceTwoA;
- private static IBaseResource ourLastResourceTwoB;
- @Autowired
- private InterceptorService myInterceptorRegistry;
- @Autowired
- private MyTestInterceptorOne myInterceptorOne;
- @Autowired
- private MyTestInterceptorTwo myInterceptorTwo;
- @Autowired
- private MyTestInterceptorManual myInterceptorManual;
-
- @Test
- public void testRegisterHookFails() {
- int initialSize = myInterceptorRegistry.getGlobalInterceptorsForUnitTest().size();
-
- try {
- myInterceptorRegistry.registerInterceptor(new InterceptorThatFailsOnRegister());
- fail();
- } catch (InternalErrorException e) {
- // good
- }
-
- assertEquals(initialSize, myInterceptorRegistry.getGlobalInterceptorsForUnitTest().size());
-
- }
-
- @Test
- public void testGlobalInterceptorsAreFound() {
- List globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest();
- assertEquals(2, globalInterceptors.size());
- assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
- assertTrue(globalInterceptors.get(1).getClass().toString(), globalInterceptors.get(1) instanceof MyTestInterceptorTwo);
- }
-
- @Test
- public void testManuallyRegisterGlobalInterceptor() {
-
- // Register the manual interceptor (has @Order right in the middle)
- myInterceptorRegistry.registerInterceptor(myInterceptorManual);
- List globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest();
- assertEquals(3, globalInterceptors.size());
- assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
- assertTrue(globalInterceptors.get(1).getClass().toString(), globalInterceptors.get(1) instanceof MyTestInterceptorManual);
- assertTrue(globalInterceptors.get(2).getClass().toString(), globalInterceptors.get(2) instanceof MyTestInterceptorTwo);
-
- // Try to register again (should have no effect
- myInterceptorRegistry.registerInterceptor(myInterceptorManual);
- globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest();
- assertEquals(3, globalInterceptors.size());
- assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
- assertTrue(globalInterceptors.get(1).getClass().toString(), globalInterceptors.get(1) instanceof MyTestInterceptorManual);
- assertTrue(globalInterceptors.get(2).getClass().toString(), globalInterceptors.get(2) instanceof MyTestInterceptorTwo);
-
- // Make sure we have the right invokers in the right order
- List invokers = myInterceptorRegistry.getInterceptorsWithInvokersForPointcut(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED);
- assertSame(myInterceptorOne, invokers.get(0));
- assertSame(myInterceptorManual, invokers.get(1));
- assertSame(myInterceptorTwo, invokers.get(2));
-
- // Finally, unregister it
- myInterceptorRegistry.unregisterInterceptor(myInterceptorManual);
- globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest();
- assertEquals(2, globalInterceptors.size());
- assertTrue(globalInterceptors.get(0).getClass().toString(), globalInterceptors.get(0) instanceof MyTestInterceptorOne);
- assertTrue(globalInterceptors.get(1).getClass().toString(), globalInterceptors.get(1) instanceof MyTestInterceptorTwo);
-
- }
-
- @Test
- public void testInvokeGlobalInterceptorMethods() {
- Patient patient = new Patient();
- HookParams params = new HookParams()
- .add(IBaseResource.class, patient);
- boolean outcome = myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- assertTrue(outcome);
-
- assertThat(ourInvocations, contains("MyTestInterceptorOne.beforeRestHookDelivery", "MyTestInterceptorTwo.beforeRestHookDelivery"));
- assertSame(patient, ourLastResourceTwoA);
- assertNull(ourLastResourceTwoB);
- assertSame(patient, ourLastResourceOne);
- }
-
- @Test
- public void testInvokeUsingSupplierArg() {
- Patient patient = new Patient();
- HookParams params = new HookParams()
- .addSupplier(IBaseResource.class, () -> patient);
- boolean outcome = myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- assertTrue(outcome);
-
- assertThat(ourInvocations, contains("MyTestInterceptorOne.beforeRestHookDelivery", "MyTestInterceptorTwo.beforeRestHookDelivery"));
- assertSame(patient, ourLastResourceTwoA);
- assertNull(ourLastResourceTwoB);
- assertSame(patient, ourLastResourceOne);
- }
-
- @Test
- public void testInvokeGlobalInterceptorMethods_MethodAbortsProcessing() {
- ourNext_beforeRestHookDelivery_Return1 = false;
-
- Patient patient = new Patient();
- HookParams params = new HookParams()
- .add(IBaseResource.class, patient);
- boolean outcome = myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- assertFalse(outcome);
-
- assertThat(ourInvocations, contains("MyTestInterceptorOne.beforeRestHookDelivery"));
- }
-
- @Test
- public void testCallHooksInvokedWithWrongParameters() {
- Integer msg = 123;
- CanonicalSubscription subs = new CanonicalSubscription();
- HookParams params = new HookParams(msg, subs);
- try {
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- fail();
- } catch (IllegalArgumentException e) {
- assertEquals("Wrong number of params for pointcut OP_PRECOMMIT_RESOURCE_CREATED - Wanted org.hl7.fhir.instance.model.api.IBaseResource but found [CanonicalSubscription, Integer]", e.getMessage());
- }
- }
-
- @Test
- public void testValidateParamTypes() {
- HookParams params = new HookParams();
- params.add(IBaseResource.class, new Patient());
- params.add(IBaseResource.class, new Patient());
- boolean validated = myInterceptorRegistry.haveAppropriateParams(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, params);
- assertTrue(validated);
- }
-
- @Test
- public void testValidateParamTypesMissingParam() {
- HookParams params = new HookParams();
- params.add(IBaseResource.class, new Patient());
- try {
- myInterceptorRegistry.haveAppropriateParams(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, params);
- fail();
- } catch (IllegalArgumentException e) {
- assertEquals("Wrong number of params for pointcut OP_PRECOMMIT_RESOURCE_UPDATED - Wanted org.hl7.fhir.instance.model.api.IBaseResource,org.hl7.fhir.instance.model.api.IBaseResource but found [Patient]", e.getMessage());
- }
- }
-
- @Test
- public void testValidateParamTypesExtraParam() {
- HookParams params = new HookParams();
- params.add(IBaseResource.class, new Patient());
- params.add(IBaseResource.class, new Patient());
- params.add(IBaseResource.class, new Patient());
- try {
- myInterceptorRegistry.haveAppropriateParams(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, params);
- fail();
- } catch (IllegalArgumentException e) {
- assertEquals("Wrong number of params for pointcut OP_PRECOMMIT_RESOURCE_UPDATED - Wanted org.hl7.fhir.instance.model.api.IBaseResource,org.hl7.fhir.instance.model.api.IBaseResource but found [Patient, Patient, Patient]", e.getMessage());
- }
- }
-
- @SuppressWarnings("unchecked")
- @Test
- public void testValidateParamTypesWrongParam() {
- HookParams params = new HookParams();
- Class clazz = IBaseResource.class;
- params.add(clazz, "AAA");
- params.add(clazz, "BBB");
- try {
- myInterceptorRegistry.haveAppropriateParams(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, params);
- fail();
- } catch (IllegalArgumentException e) {
- assertEquals("Invalid params for pointcut OP_PRECOMMIT_RESOURCE_UPDATED - class java.lang.String is not of type interface org.hl7.fhir.instance.model.api.IBaseResource", e.getMessage());
- }
- }
-
- @Test
- public void testThreadLocalHookInterceptor() {
- myInterceptorRegistry.setThreadlocalInvokersEnabled(true);
-
- Patient patient = new Patient();
- HookParams params = new HookParams().add(IBaseResource.class, patient);
-
- @Interceptor
- @Order(100)
- class LocalInterceptor {
-
- private int myCount = 0;
-
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED)
- public boolean beforeRestHookDelivery(IBaseResource theResource) {
- myCount++;
- return true;
- }
-
- }
- LocalInterceptor interceptor = new LocalInterceptor();
- myInterceptorRegistry.registerThreadLocalInterceptor(interceptor);
- try {
-
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- assertEquals(5, interceptor.myCount);
-
- } finally {
- myInterceptorRegistry.unregisterThreadLocalInterceptor(interceptor);
- }
-
- // Call some more - The interceptor is removed so the count shouldn't change
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- assertEquals(5, interceptor.myCount);
-
- }
-
- /**
- *
- * JA 20190321 On my MBP 2018
- * ThreadLocalEnabled=true - Performed 500000 loops in 8383.0ms - 0.017ms / loop
- * ThreadLocalEnabled=false - Performed 500000 loops in 3743.0ms - 0.007ms / loop
- * ThreadLocalEnabled=true - Performed 500000 loops in 6163.0ms - 0.012ms / loop
- * ThreadLocalEnabled=false - Performed 500000 loops in 3487.0ms - 0.007ms / loop
- * ThreadLocalEnabled=true - Performed 1000000 loops in 00:00:12.458 - 0.012ms / loop
- * ThreadLocalEnabled=false - Performed 1000000 loops in 7046.0ms - 0.007ms / loop
- *
- */
- @Test
- @Ignore("Performance test - Not needed normally")
- public void testThreadLocalHookInterceptorMicroBenchmark() {
- threadLocalMicroBenchmark(true, 500000);
- threadLocalMicroBenchmark(false, 500000);
- threadLocalMicroBenchmark(true, 500000);
- threadLocalMicroBenchmark(false, 500000);
- threadLocalMicroBenchmark(true, 1000000);
- threadLocalMicroBenchmark(false, 1000000);
- }
-
- private void threadLocalMicroBenchmark(boolean theThreadlocalInvokersEnabled, int theCount) {
- myInterceptorRegistry.setThreadlocalInvokersEnabled(theThreadlocalInvokersEnabled);
-
- Patient patient = new Patient();
- HookParams params = new HookParams().add(IBaseResource.class, patient);
-
- @Interceptor
- @Order(100)
- class LocalInterceptor {
-
- private int myCount = 0;
-
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED)
- public boolean beforeRestHookDelivery(IBaseResource theResource) {
- myCount++;
- return true;
- }
-
- }
-
- StopWatch sw = new StopWatch();
- for (int i = 0; i < theCount; i++) {
-
- LocalInterceptor interceptor = new LocalInterceptor();
- myInterceptorRegistry.registerThreadLocalInterceptor(interceptor);
- try {
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- myInterceptorRegistry.callHooks(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, params);
- } finally {
- myInterceptorRegistry.unregisterThreadLocalInterceptor(interceptor);
- }
-
- }
-
- ourLog.info("ThreadLocalEnabled={} - Performed {} loops in {} - {} / loop", theThreadlocalInvokersEnabled, theCount, sw.toString(), sw.formatMillisPerOperation(theCount));
- }
-
- @Before
- public void before() {
- ourNext_beforeRestHookDelivery_Return1 = true;
- ourLastResourceOne = null;
- ourLastResourceTwoA = null;
- ourLastResourceTwoB = null;
- ourInvocations.clear();
- }
-
- @After
- public void after() {
- myInterceptorRegistry.setThreadlocalInvokersEnabled(new InterceptorService().isThreadlocalInvokersEnabled());
- }
-
- @Configuration
- static class InterceptorRegistryTestCtxConfig {
-
- @Bean
- public InterceptorService interceptorService() {
- return new InterceptorService("test");
- }
-
- /**
- * Note: Orders are deliberately reversed to make sure we get the orders right
- * using the @Order annotation
- */
- @Bean
- public MyTestInterceptorTwo interceptor1() {
- MyTestInterceptorTwo retVal = new MyTestInterceptorTwo();
- interceptorService().registerInterceptor(retVal);
- return retVal;
- }
-
- /**
- * Note: Orders are deliberately reversed to make sure we get the orders right
- * using the @Order annotation
- */
- @Bean
- public MyTestInterceptorOne interceptor2() {
- MyTestInterceptorOne retVal = new MyTestInterceptorOne();
- interceptorService().registerInterceptor(retVal);
- return retVal;
- }
-
- @Bean
- public MyTestInterceptorManual interceptorManual() {
- return new MyTestInterceptorManual();
- }
-
- }
-
- @Interceptor
- @Order(100)
- public static class MyTestInterceptorOne {
-
- public MyTestInterceptorOne() {
- super();
- }
-
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED)
- public boolean beforeRestHookDelivery(IBaseResource theResource) {
- ourLastResourceOne = theResource;
- ourInvocations.add("MyTestInterceptorOne.beforeRestHookDelivery");
- return ourNext_beforeRestHookDelivery_Return1;
- }
-
- }
-
- @Interceptor
- @Order(300)
- public static class MyTestInterceptorTwo {
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED)
- public void beforeRestHookDelivery(IBaseResource theResource0, IBaseResource theResource1) {
- ourLastResourceTwoA = theResource0;
- ourLastResourceTwoB = theResource1;
- ourInvocations.add("MyTestInterceptorTwo.beforeRestHookDelivery");
- }
- }
-
- @Interceptor(manualRegistration = true)
- @Order(200)
- public static class MyTestInterceptorManual {
- @Hook(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED)
- public void beforeRestHookDelivery() {
- ourInvocations.add("MyTestInterceptorManual.beforeRestHookDelivery");
- }
- }
-
- /**
- * Just a make-believe version of this class for the unit test
- */
- private static class CanonicalSubscription {
- }
-
- /**
- * Just a make-believe version of this class for the unit test
- */
- private static class ResourceDeliveryMessage {
- }
-
- @Interceptor(manualRegistration = true)
- public static class InterceptorThatFailsOnRegister {
-
- @Hook(Pointcut.REGISTERED)
- public void start() throws Exception {
- throw new Exception("InterceptorThatFailsOnRegister FAILED!");
- }
-
- }
-
-}
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/BaseSearchParamRegistry.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/BaseSearchParamRegistry.java
index 3b613beeab7..e7ba4cf9c05 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/BaseSearchParamRegistry.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/BaseSearchParamRegistry.java
@@ -23,10 +23,11 @@ package ca.uhn.fhir.jpa.searchparam.registry;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
-import ca.uhn.fhir.jpa.model.search.PerformanceMessage;
+import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.retry.Retrier;
@@ -186,7 +187,9 @@ public abstract class BaseSearchParamRegistry implemen
.collect(Collectors.joining(", "));
String message = "Search parameter " + next.getId().toUnqualifiedVersionless().getValue() + " refers to unknown component " + nextRef + ", ignoring this parameter (valid values: " + existingParams + ")";
ourLog.warn(message);
- myInterceptorBroadcaster.callHooks(Pointcut.PERFTRACE_MESSAGE, new PerformanceMessage().setMessage(message));
+ StorageProcessingMessage msg = new StorageProcessingMessage().setMessage(message);
+ HookParams params = new HookParams(msg);
+ myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PROCESSING_MESSAGE, params);
}
}
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/IPointcutLatch.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/IPointcutLatch.java
index d0af4d01d25..844fbc02344 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/IPointcutLatch.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/IPointcutLatch.java
@@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.subscription.module;
*/
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.HookParams;
import java.util.List;
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistry.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistry.java
index c81fde758d0..5c0d8e1d522 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistry.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistry.java
@@ -20,9 +20,10 @@ package ca.uhn.fhir.jpa.subscription.module.cache;
* #L%
*/
+import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -108,7 +109,9 @@ public class SubscriptionRegistry {
myActiveSubscriptionCache.put(subscriptionId, activeSubscription);
// Interceptor call: SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED
- myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, canonicalized);
+ HookParams params = new HookParams()
+ .add(CanonicalSubscription.class, canonicalized);
+ myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, params);
return canonicalized;
}
@@ -168,7 +171,9 @@ public class SubscriptionRegistry {
activeSubscription.setSubscription(canonicalized);
// Interceptor call: SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED
- myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, canonicalized);
+ HookParams params = new HookParams()
+ .add(CanonicalSubscription.class, canonicalized);
+ myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, params);
}
private boolean channelTypeSame(CanonicalSubscription theExistingSubscription, CanonicalSubscription theNewSubscription) {
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java
index 078133af408..8faa8321aeb 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/BaseSubscriptionConfig.java
@@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.subscription.module.config;
*/
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.model.interceptor.executor.InterceptorService;
+import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.jpa.subscription.module.cache.ISubscribableChannelFactory;
import ca.uhn.fhir.jpa.subscription.module.cache.LinkedBlockingQueueSubscribableChannelFactory;
import org.springframework.context.annotation.Bean;
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/interceptor/SubscriptionDebugLogInterceptor.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/interceptor/SubscriptionDebugLogInterceptor.java
index c6e7afa0933..344b342f050 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/interceptor/SubscriptionDebugLogInterceptor.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/interceptor/SubscriptionDebugLogInterceptor.java
@@ -20,9 +20,9 @@ package ca.uhn.fhir.jpa.subscription.module.interceptor;
* #L%
*/
-import ca.uhn.fhir.jpa.model.interceptor.api.Hook;
-import ca.uhn.fhir.jpa.model.interceptor.api.Interceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.Hook;
+import ca.uhn.fhir.interceptor.api.Interceptor;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscriptionChannelType;
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
import ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchResult;
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/BaseSubscriptionDeliverySubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/BaseSubscriptionDeliverySubscriber.java
index 820a255950a..d5e5533f981 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/BaseSubscriptionDeliverySubscriber.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/BaseSubscriptionDeliverySubscriber.java
@@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.subscription.module.subscriber;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,9 +21,10 @@ package ca.uhn.fhir.jpa.subscription.module.subscriber;
*/
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.module.cache.ActiveSubscription;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
import org.slf4j.Logger;
@@ -65,14 +66,17 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl
try {
// Interceptor call: SUBSCRIPTION_BEFORE_DELIVERY
- if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_DELIVERY, msg, msg.getSubscription())) {
+ HookParams params = new HookParams()
+ .add(ResourceDeliveryMessage.class, msg)
+ .add(CanonicalSubscription.class, msg.getSubscription());
+ if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_DELIVERY, params)) {
return;
}
handleMessage(msg);
// Interceptor call: SUBSCRIPTION_AFTER_DELIVERY
- myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_DELIVERY, msg, msg.getSubscription());
+ myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_DELIVERY, params);
} catch (Exception e) {
@@ -81,7 +85,8 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl
// Interceptor call: SUBSCRIPTION_AFTER_DELIVERY
HookParams hookParams = new HookParams()
- .add(ResourceDeliveryMessage.class, msg).add(Exception.class, e);
+ .add(ResourceDeliveryMessage.class, msg)
+ .add(Exception.class, e);
if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_DELIVERY_FAILED, hookParams)) {
return;
}
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionDeliveringRestHookSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionDeliveringRestHookSubscriber.java
index 7c312368158..0ac6272ea20 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionDeliveringRestHookSubscriber.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionDeliveringRestHookSubscriber.java
@@ -20,10 +20,10 @@ package ca.uhn.fhir.jpa.subscription.module.subscriber;
* #L%
*/
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.*;
@@ -138,7 +138,10 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
CanonicalSubscription subscription = theMessage.getSubscription();
// Interceptor call: SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY
- if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY, theMessage, subscription)) {
+ HookParams params = new HookParams()
+ .add(CanonicalSubscription.class, subscription)
+ .add(ResourceDeliveryMessage.class, theMessage);
+ if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY, params)) {
return;
}
@@ -174,7 +177,10 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
deliverPayload(theMessage, subscription, payloadType, client);
// Interceptor call: SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY
- if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY, theMessage, subscription)) {
+ params = new HookParams()
+ .add(CanonicalSubscription.class, subscription)
+ .add(ResourceDeliveryMessage.class, theMessage);
+ if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY, params)) {
//noinspection UnnecessaryReturnStatement
return;
}
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriber.java
index 3345535d7b6..b4a424d82f2 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriber.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriber.java
@@ -1,8 +1,10 @@
package ca.uhn.fhir.jpa.subscription.module.subscriber;
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
import ca.uhn.fhir.jpa.subscription.module.cache.ActiveSubscription;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
@@ -33,9 +35,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -85,7 +87,9 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
}
// Interceptor call: SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED
- if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED, theMsg)) {
+ HookParams params = new HookParams()
+ .add(ResourceModifiedMessage.class, theMsg);
+ if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED, params)) {
return;
}
@@ -93,7 +97,7 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
doMatchActiveSubscriptionsAndDeliver(theMsg);
} finally {
// Interceptor call: SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED
- myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED, theMsg);
+ myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED, params);
}
}
@@ -141,7 +145,11 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
}
// Interceptor call: SUBSCRIPTION_RESOURCE_MATCHED
- if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MATCHED, deliveryMsg, nextActiveSubscription.getSubscription(), matchResult)) {
+ HookParams params = new HookParams()
+ .add(CanonicalSubscription.class, nextActiveSubscription.getSubscription())
+ .add(ResourceDeliveryMessage.class, deliveryMsg)
+ .add(SubscriptionMatchResult.class, matchResult);
+ if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MATCHED, params)) {
return;
}
@@ -157,7 +165,9 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
if (!resourceMatched) {
// Interceptor call: SUBSCRIPTION_RESOURCE_MATCHED
- myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS, theMsg);
+ HookParams params = new HookParams()
+ .add(ResourceModifiedMessage.class, theMsg);
+ myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS, params);
}
}
diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/BaseSubscriptionTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/BaseSubscriptionTest.java
index 06cef734fbe..1bd5e7cc7c5 100644
--- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/BaseSubscriptionTest.java
+++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/BaseSubscriptionTest.java
@@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.subscription.module;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.module.config.MockFhirClientSearchParamProvider;
@@ -28,11 +28,11 @@ public abstract class BaseSubscriptionTest {
@Autowired
protected
- IInterceptorRegistry myInterceptorRegistry;
+ IInterceptorService myInterceptorRegistry;
@After
public void afterClearAnonymousLambdas() {
- myInterceptorRegistry.clearAnonymousHookForUnitTest();
+ myInterceptorRegistry.unregisterAllInterceptors();
}
public void initSearchParamRegistry(IBundleProvider theBundleProvider) {
diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/PointcutLatch.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/PointcutLatch.java
index 42fa677a9f1..2609972e071 100644
--- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/PointcutLatch.java
+++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/PointcutLatch.java
@@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.subscription.module;
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
-import ca.uhn.fhir.jpa.model.interceptor.api.IAnonymousInterceptor;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java
index 19ebdbbe6de..3b51f70082d 100644
--- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java
+++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java
@@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.subscription.module.standalone;
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
-import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry;
-import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.module.BaseSubscriptionDstu3Test;
import ca.uhn.fhir.jpa.subscription.module.IPointcutLatch;
import ca.uhn.fhir.jpa.subscription.module.PointcutLatch;
@@ -51,7 +51,7 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base
@Autowired
SubscriptionChannelFactory mySubscriptionChannelFactory;
@Autowired
- IInterceptorRegistry myInterceptorRegistry;
+ IInterceptorService myInterceptorRegistry;
@Autowired
protected SubscriptionRegistry mySubscriptionRegistry;
@@ -85,7 +85,7 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base
@After
public void cleanup() {
- myInterceptorRegistry.clearAnonymousHookForUnitTest();
+ myInterceptorRegistry.unregisterAllInterceptors();
mySubscriptionMatchingPost.clear();
mySubscriptionActivatedPost.clear();
ourObservationListener.clear();
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java
index 5c543b0017d..bb0c47af68f 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java
@@ -1,6 +1,5 @@
package ca.uhn.fhirtest.config;
-import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -14,9 +13,10 @@ public class CommonConfig {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
+ * @return
*/
@Bean
- public IServerInterceptor accessLoggingInterceptor() {
+ public LoggingInterceptor accessLoggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
@@ -50,7 +50,7 @@ public class CommonConfig {
* Do some fancy logging to create a nice access log that has details about each incoming request.
*/
@Bean
- public IServerInterceptor requestLoggingInterceptor() {
+ public LoggingInterceptor requestLoggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.request");
retVal.setMessageFormat("${requestVerb} ${servletPath} -\n${requestBodyFhir}");
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java
index 9ad6c6960d7..907863a8e63 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java
@@ -14,7 +14,6 @@ import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhirtest.interceptor.TdlSecurityInterceptor;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
-import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -53,7 +52,7 @@ public class TdlDstu2Config extends BaseJavaConfigDstu2 {
}
@Bean
- public IServerInterceptor securityInterceptor() {
+ public TdlSecurityInterceptor securityInterceptor() {
return new TdlSecurityInterceptor();
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java
index a9f6c931bfc..96ccd4a239a 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu3Config.java
@@ -14,7 +14,6 @@ import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhirtest.interceptor.TdlSecurityInterceptor;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
-import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -148,7 +147,7 @@ public class TdlDstu3Config extends BaseJavaConfigDstu3 {
}
@Bean
- public IServerInterceptor securityInterceptor() {
+ public TdlSecurityInterceptor securityInterceptor() {
return new TdlSecurityInterceptor();
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
index abdc4088009..5dfdaa66203 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
@@ -5,7 +5,6 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
-import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
@@ -51,7 +50,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
}
@Bean
- public IServerInterceptor securityInterceptor() {
+ public PublicSecurityInterceptor securityInterceptor() {
return new PublicSecurityInterceptor();
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
index 6797baa532e..89375d5a199 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
@@ -6,14 +6,12 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
-import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.dialect.PostgreSQL94Dialect;
-import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -83,7 +81,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
@Bean
- public IServerInterceptor securityInterceptor() {
+ public PublicSecurityInterceptor securityInterceptor() {
return new PublicSecurityInterceptor();
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
index 1c50c671d3f..aad2641af0d 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
@@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
-import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
@@ -144,7 +143,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
}
@Bean
- public IServerInterceptor securityInterceptor() {
+ public PublicSecurityInterceptor securityInterceptor() {
return new PublicSecurityInterceptor();
}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRequestOperationCallback.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRequestOperationCallback.java
deleted file mode 100644
index aad7808a54e..00000000000
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRequestOperationCallback.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package ca.uhn.fhir.rest.api.server;
-
-/*
- * #%L
- * HAPI FHIR - Server Framework
- * %%
- * Copyright (C) 2014 - 2019 University Health Network
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-
-import org.hl7.fhir.instance.model.api.IBaseResource;
-
-import java.util.Collection;
-
-public interface IRequestOperationCallback {
-
- void resourceCreated(IBaseResource theResource);
-
- void resourceDeleted(IBaseResource theResource);
-
- void resourcePreCreate(IBaseResource theResource);
-
- void resourcePreDelete(IBaseResource theResource);
-
- void resourcePreUpdate(IBaseResource theOldResource, IBaseResource theNewResource);
-
- /**
- * @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
- */
- @Deprecated
- void resourceUpdated(IBaseResource theResource);
-
- void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource);
-
- void resourcesCreated(Collection extends IBaseResource> theResource);
-
- void resourcesDeleted(Collection extends IBaseResource> theResource);
-
- /**
- * @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
- */
- @Deprecated
- void resourcesUpdated(Collection extends IBaseResource> theResource);
-}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java
index 24ccfde0e8c..e85072308a1 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java
@@ -1,17 +1,19 @@
package ca.uhn.fhir.rest.api.server;
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
-import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.Validate;
-import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
+import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -46,6 +48,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class RequestDetails {
+ private IInterceptorBroadcaster myInterceptorBroadcaster;
private String myTenantId;
private String myCompartmentName;
private String myCompleteUrl;
@@ -54,7 +57,7 @@ public abstract class RequestDetails {
private String myOperation;
private Map myParameters;
private byte[] myRequestContents;
- private IRequestOperationCallback myRequestOperationCallback;
+ private DeferredOperationCallback myDeferredInterceptorBroadcaster;
private String myRequestPath;
private RequestTypeEnum myRequestType;
private String myResourceName;
@@ -69,8 +72,8 @@ public abstract class RequestDetails {
/**
* Constructor
*/
- public RequestDetails() {
- myRequestOperationCallback = new RequestOperationCallback();
+ public RequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) {
+ myInterceptorBroadcaster = theInterceptorBroadcaster;
}
public void addParameter(String theName, String[] theValues) {
@@ -242,8 +245,11 @@ public abstract class RequestDetails {
* of any nested operations being invoked within operations. This invoker acts as a proxy for
* all interceptors
*/
- public IRequestOperationCallback getRequestOperationCallback() {
- return myRequestOperationCallback;
+ public IInterceptorBroadcaster getInterceptorBroadcaster() {
+ if (myDeferredInterceptorBroadcaster != null) {
+ return myDeferredInterceptorBroadcaster;
+ }
+ return myInterceptorBroadcaster;
}
/**
@@ -427,194 +433,57 @@ public abstract class RequestDetails {
}
/**
- * Sets the {@link #getRequestOperationCallback() requestOperationCallback} handler in
+ * Sets the {@link #getInterceptorBroadcaster()} () interceptor broadcaster} handler in
* deferred mode, meaning that any notifications will be queued up for delivery, but
* won't be delivered until {@link #stopDeferredRequestOperationCallbackAndRunDeferredItems()}
* is called.
*/
public void startDeferredOperationCallback() {
- myRequestOperationCallback = new DeferredOperationCallback(myRequestOperationCallback);
+ myDeferredInterceptorBroadcaster = new DeferredOperationCallback(myInterceptorBroadcaster);
}
/**
* @see #startDeferredOperationCallback()
*/
public void stopDeferredRequestOperationCallbackAndRunDeferredItems() {
- DeferredOperationCallback deferredCallback = (DeferredOperationCallback) myRequestOperationCallback;
+ DeferredOperationCallback deferredCallback = myDeferredInterceptorBroadcaster;
deferredCallback.playDeferredActions();
- myRequestOperationCallback = deferredCallback.getWrap();
+ myInterceptorBroadcaster = deferredCallback.getWrap();
}
- private class DeferredOperationCallback implements IRequestOperationCallback {
+ private class DeferredOperationCallback implements IInterceptorBroadcaster {
- private final IRequestOperationCallback myWrap;
+ private final IInterceptorBroadcaster myWrap;
private final List myDeferredTasks = new ArrayList<>();
- private DeferredOperationCallback(IRequestOperationCallback theWrap) {
+ private DeferredOperationCallback(@Nonnull IInterceptorBroadcaster theWrap) {
+ Validate.notNull(theWrap);
myWrap = theWrap;
}
- @Override
- public void resourceCreated(IBaseResource theResource) {
- myDeferredTasks.add(()-> myWrap.resourceCreated(theResource));
- }
-
- @Override
- public void resourceDeleted(IBaseResource theResource) {
- myDeferredTasks.add(()-> myWrap.resourceDeleted(theResource));
- }
-
- @Override
- public void resourcePreCreate(IBaseResource theResource) {
- myWrap.resourcePreCreate(theResource);
- }
-
- @Override
- public void resourcePreDelete(IBaseResource theResource) {
- myWrap.resourcePreDelete(theResource);
- }
-
- @Override
- public void resourcePreUpdate(IBaseResource theOldResource, IBaseResource theNewResource) {
- myWrap.resourcePreUpdate(theOldResource, theNewResource);
- }
-
- @Override
- public void resourceUpdated(IBaseResource theResource) {
- myDeferredTasks.add(()-> myWrap.resourceUpdated(theResource));
- }
-
- @Override
- public void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource) {
- myDeferredTasks.add(()-> myWrap.resourceUpdated(theOldResource, theNewResource));
- }
-
- @Override
- public void resourcesCreated(Collection extends IBaseResource> theResource) {
- myDeferredTasks.add(()-> myWrap.resourcesCreated(theResource));
- }
-
- @Override
- public void resourcesDeleted(Collection extends IBaseResource> theResource) {
- myDeferredTasks.add(()-> myWrap.resourcesDeleted(theResource));
- }
-
- @Override
- public void resourcesUpdated(Collection extends IBaseResource> theResource) {
- myDeferredTasks.add(()-> myWrap.resourcesUpdated(theResource));
- }
void playDeferredActions() {
myDeferredTasks.forEach(Runnable::run);
}
- IRequestOperationCallback getWrap() {
+ IInterceptorBroadcaster getWrap() {
return myWrap;
}
- }
- private class RequestOperationCallback implements IRequestOperationCallback {
-
- private List getInterceptors() {
- if (getServer() == null) {
- return Collections.emptyList();
- }
- return getServer().getInterceptors();
+ @Override
+ public boolean callHooks(Pointcut thePointcut, HookParams theParams) {
+ myDeferredTasks.add(() -> myWrap.callHooks(thePointcut, theParams));
+ return true;
}
@Override
- public void resourceCreated(IBaseResource theResource) {
- for (IServerInterceptor next : getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceCreated(RequestDetails.this, theResource);
- }
- }
- }
-
- @Override
- public void resourceDeleted(IBaseResource theResource) {
- for (IServerInterceptor next : getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceDeleted(RequestDetails.this, theResource);
- }
- }
- }
-
- @Override
- public void resourcePreCreate(IBaseResource theResource) {
- for (IServerInterceptor next : getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreCreate(RequestDetails.this, theResource);
- }
- }
- }
-
- @Override
- public void resourcePreDelete(IBaseResource theResource) {
- for (IServerInterceptor next : getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreDelete(RequestDetails.this, theResource);
- }
- }
- }
-
- @Override
- public void resourcePreUpdate(IBaseResource theOldResource, IBaseResource theNewResource) {
- for (IServerInterceptor next : getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourcePreUpdate(RequestDetails.this, theOldResource, theNewResource);
- }
- }
- }
-
- /**
- * @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
- */
- @Deprecated
- @Override
- public void resourceUpdated(IBaseResource theResource) {
- for (IServerInterceptor next : getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceUpdated(RequestDetails.this, theResource);
- }
- }
- }
-
- @Override
- public void resourceUpdated(IBaseResource theOldResource, IBaseResource theNewResource) {
- for (IServerInterceptor next : getInterceptors()) {
- if (next instanceof IServerOperationInterceptor) {
- ((IServerOperationInterceptor) next).resourceUpdated(RequestDetails.this, theOldResource, theNewResource);
- }
- }
- }
-
- @Override
- public void resourcesCreated(Collection extends IBaseResource> theResource) {
- for (IBaseResource next : theResource) {
- resourceCreated(next);
- }
- }
-
- @Override
- public void resourcesDeleted(Collection extends IBaseResource> theResource) {
- for (IBaseResource next : theResource) {
- resourceDeleted(next);
- }
- }
-
- /**
- * @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead
- */
- @Override
- @Deprecated
- public void resourcesUpdated(Collection extends IBaseResource> theResource) {
- for (IBaseResource next : theResource) {
- resourceUpdated(next);
- }
+ public Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams) {
+ myDeferredTasks.add(() -> myWrap.callHooksAndReturnObject(thePointcut, theParams));
+ return null;
}
}
+
}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BasePagingProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BasePagingProvider.java
index e47a2281b6b..39c9355ae78 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BasePagingProvider.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BasePagingProvider.java
@@ -24,8 +24,11 @@ import org.apache.commons.lang3.Validate;
public abstract class BasePagingProvider implements IPagingProvider {
- private int myDefaultPageSize = 10;
- private int myMaximumPageSize = 50;
+ public static final int DEFAULT_DEFAULT_PAGE_SIZE = 10;
+ public static final int DEFAULT_MAX_PAGE_SIZE = 50;
+
+ private int myDefaultPageSize = DEFAULT_DEFAULT_PAGE_SIZE;
+ private int myMaximumPageSize = DEFAULT_MAX_PAGE_SIZE;
public BasePagingProvider() {
super();
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java
index 246ccf68c81..4463d47496c 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java
@@ -1,5 +1,11 @@
package ca.uhn.fhir.rest.server;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.api.AddProfileTagEnum;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.rest.api.EncodingEnum;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+
import java.util.List;
/*
@@ -22,30 +28,25 @@ import java.util.List;
* #L%
*/
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.context.api.AddProfileTagEnum;
-import ca.uhn.fhir.rest.api.EncodingEnum;
-import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
-
public interface IRestfulServerDefaults {
/**
* @return Returns the setting for automatically adding profile tags
* @deprecated As of HAPI FHIR 1.5, this property has been moved to
- * {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
+ * {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/
@Deprecated
AddProfileTagEnum getAddProfileTag();
/**
* @return Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference
- * (either with the _format URL parameter, or with an Accept header
- * in the request. The default is {@link EncodingEnum#XML}. Will not return null.
+ * (either with the _format URL parameter, or with an Accept header
+ * in the request. The default is {@link EncodingEnum#XML}. Will not return null.
*/
EncodingEnum getDefaultResponseEncoding();
/**
* @return Returns the server support for ETags (will not be null). Default is
- * {@link RestfulServer#DEFAULT_ETAG_SUPPORT}
+ * {@link RestfulServer#DEFAULT_ETAG_SUPPORT}
*/
ETagSupportEnum getETagSupport();
@@ -66,7 +67,7 @@ public interface IRestfulServerDefaults {
/**
* Returns the list of interceptors registered against this server
*/
- List getInterceptors();
+ List getInterceptors_();
/**
* Returns the paging provider for this server
@@ -80,11 +81,13 @@ public interface IRestfulServerDefaults {
*
* The default is false
*
- *
+ *
* @return Returns the default pretty print setting
*/
boolean isDefaultPrettyPrint();
-
-
+ /**
+ * Returns the interceptor service for this server
+ */
+ IInterceptorService getInterceptorService();
}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
index 79c39f9369d..cca3799e2d4 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
@@ -26,6 +26,10 @@ import ca.uhn.fhir.context.ProvidedResourceScanner;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.context.api.BundleInclusionRule;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.Destroy;
@@ -46,7 +50,6 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy;
import ca.uhn.fhir.util.*;
import com.google.common.collect.Lists;
-import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -54,6 +57,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
@@ -71,6 +75,7 @@ import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.Manifest;
+import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -100,9 +105,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer myInterceptors = new ArrayList<>();
private final List myPlainProviders = new ArrayList<>();
private final List myResourceProviders = new ArrayList<>();
+ private IInterceptorService myInterceptorService;
private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES;
private boolean myDefaultPrettyPrint = false;
private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML;
@@ -126,7 +131,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer getInterceptors_() {
+ List retVal = getInterceptorService()
+ .getAllRegisteredInterceptors()
+ .stream()
+ .filter(t -> t instanceof IServerInterceptor)
+ .map(t -> (IServerInterceptor) t)
+ .collect(Collectors.toList());
+ return Collections.unmodifiableList(retVal);
+ }
+
+ /**
+ * Returns the interceptor registry for this service. Use this registry to register and unregister
+ * @since 3.8.0
*/
@Override
- public List getInterceptors() {
- return Collections.unmodifiableList(myInterceptors);
+ public IInterceptorService getInterceptorService() {
+ return myInterceptorService;
+ }
+
+ /**
+ * Sets the interceptor registry for this service. Use this registry to register and unregister
+ *
+ * @since 3.8.0
+ */
+ public void setInterceptorService(@Nonnull IInterceptorService theInterceptorService) {
+ Validate.notNull(theInterceptorService, "theInterceptorService must not be null");
+ myInterceptorService = theInterceptorService;
}
/**
* Sets (or clears) the list of interceptors
*
* @param theList The list of interceptors (may be null)
+ * @deprecated As of HAPI FHIR 3.8.0, use {@link #getInterceptorService()} to access the interceptor service. You can register and unregister interceptors using this service.
*/
- public void setInterceptors(List theList) {
- myInterceptors.clear();
- if (theList != null) {
- myInterceptors.addAll(theList);
- }
+ @Deprecated
+ public void setInterceptors(@Nonnull List> theList) {
+ myInterceptorService.unregisterAllInterceptors();
+ myInterceptorService.registerInterceptors(theList);
}
/**
* Sets (or clears) the list of interceptors
*
* @param theInterceptors The list of interceptors (may be null)
+ * @deprecated As of HAPI FHIR 3.8.0, use {@link #getInterceptorService()} to access the interceptor service. You can register and unregister interceptors using this service.
*/
+ @Deprecated
public void setInterceptors(IServerInterceptor... theInterceptors) {
Validate.noNullElements(theInterceptors, "theInterceptors must not contain any null elements");
-
- myInterceptors.clear();
- if (theInterceptors != null) {
- myInterceptors.addAll(Arrays.asList(theInterceptors));
- }
+ setInterceptors(Arrays.asList(theInterceptors));
}
@Override
@@ -814,7 +844,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer 0 && requestPath.charAt(0) == '/') {
@@ -940,44 +968,46 @@ public class RestfulServer extends HttpServlet implements IRestfulServer= 0; i--) {
- IServerInterceptor next = getInterceptors().get(i);
- try {
- next.processingCompletedNormally(requestDetails);
- } catch (Throwable t) {
- ourLog.error("Failure in interceptor method", t);
- }
+ // Invoke interceptors
+ HookParams hookParams = new HookParams();
+ hookParams.add(RequestDetails.class, requestDetails);
+ hookParams.add(ServletRequestDetails.class, requestDetails);
+ myInterceptorService.callHooks(Pointcut.SERVER_PROCESSING_COMPLETED_NORMALLY, hookParams);
+
+ ourLog.trace("Done writing to stream: {}", outputStreamOrWriter);
}
- IOUtils.closeQuietly(outputStreamOrWriter);
-
} catch (NotModifiedException | AuthenticationException e) {
- for (int i = getInterceptors().size() - 1; i >= 0; i--) {
- IServerInterceptor next = getInterceptors().get(i);
- if (!next.handleException(requestDetails, e, theRequest, theResponse)) {
- ourLog.debug("Interceptor {} returned false, not continuing processing");
- return;
- }
+ HookParams handleExceptionParams = new HookParams();
+ handleExceptionParams.add(RequestDetails.class, requestDetails);
+ handleExceptionParams.add(ServletRequestDetails.class, requestDetails);
+ handleExceptionParams.add(HttpServletRequest.class, theRequest);
+ handleExceptionParams.add(HttpServletResponse.class, theResponse);
+ handleExceptionParams.add(BaseServerResponseException.class, e);
+ if (!myInterceptorService.callHooks(Pointcut.SERVER_HANDLE_EXCEPTION, handleExceptionParams)) {
+ return;
}
+
writeExceptionToResponse(theResponse, e);
} catch (Throwable e) {
@@ -990,15 +1020,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer= 0; i--) {
- IServerInterceptor next = getInterceptors().get(i);
- exception = next.preProcessOutgoingException(requestDetails, e, theRequest);
- if (exception != null) {
- ourLog.debug("Interceptor {} returned false, not continuing processing");
- break;
- }
- }
+ HookParams preProcessParams = new HookParams();
+ preProcessParams.add(RequestDetails.class, requestDetails);
+ preProcessParams.add(ServletRequestDetails.class, requestDetails);
+ preProcessParams.add(HttpServletRequest.class, theRequest);
+ preProcessParams.add(HttpServletResponse.class, theResponse);
+ preProcessParams.add(Throwable.class, e);
+ BaseServerResponseException exception = (BaseServerResponseException) myInterceptorService.callHooksAndReturnObject(Pointcut.SERVER_PRE_PROCESS_OUTGOING_EXCEPTION, preProcessParams);
/*
* If none of the interceptors converted the exception, default behaviour is to keep the exception as-is if it
@@ -1012,12 +1040,14 @@ public class RestfulServer extends HttpServlet implements IRestfulServer= 0; i--) {
- IServerInterceptor next = getInterceptors().get(i);
- if (!next.handleException(requestDetails, exception, theRequest, theResponse)) {
- ourLog.debug("Interceptor {} returned false, not continuing processing");
- return;
- }
+ HookParams handleExceptionParams = new HookParams();
+ handleExceptionParams.add(RequestDetails.class, requestDetails);
+ handleExceptionParams.add(ServletRequestDetails.class, requestDetails);
+ handleExceptionParams.add(HttpServletRequest.class, theRequest);
+ handleExceptionParams.add(HttpServletResponse.class, theResponse);
+ handleExceptionParams.add(BaseServerResponseException.class, exception);
+ if (!myInterceptorService.callHooks(Pointcut.SERVER_HANDLE_EXCEPTION, handleExceptionParams)) {
+ return;
}
/*
@@ -1355,11 +1385,15 @@ public class RestfulServer extends HttpServlet implements IRestfulServer resourceType = rsrcProvider.getResourceType();
- String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
providedResourceScanner.removeProvidedResources(rsrcProvider);
} else {
myPlainProviders.remove(provider);
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
index 7a43ec64d57..62bf7fbb1e7 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
@@ -21,6 +21,10 @@ package ca.uhn.fhir.rest.server.interceptor;
*/
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.interceptor.api.Hook;
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.rest.annotation.Read;
@@ -86,6 +90,7 @@ public interface IServerInterceptor {
* @throws ServletException If this exception is thrown, it will be re-thrown up to the container for handling.
* @throws IOException If this exception is thrown, it will be re-thrown up to the container for handling.
*/
+ @Hook(Pointcut.SERVER_HANDLE_EXCEPTION)
boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws ServletException, IOException;
@@ -106,6 +111,7 @@ public interface IServerInterceptor {
* @throws AuthenticationException 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.
*/
+ @Hook(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED)
boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
/**
@@ -124,6 +130,7 @@ public interface IServerInterceptor {
* @param theProcessedRequest An object which will be populated with the details which were extracted from the raw request by the
* server, e.g. the FHIR operation type and the parsed resource body (if any).
*/
+ @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED)
void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest);
/**
@@ -142,6 +149,7 @@ public interface IServerInterceptor {
* must return