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 objects = myParams.get(theParamType); Object retVal = null; @@ -115,4 +117,19 @@ public class HookParams { .map(t -> unwrapValue(t)) .collect(Collectors.toList()); } + + @SuppressWarnings("unchecked") + public HookParams addIfMatchesType(Class theType, Object theParam) { + if (theParam == null) { + add(theType, null); + } else { + if (theType.isAssignableFrom(theParam.getClass())) { + T param = (T) theParam; + add(theType, param); + } else { + add(theType, null); + } + } + return this; + } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IAnonymousInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IAnonymousInterceptor.java similarity index 93% rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IAnonymousInterceptor.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IAnonymousInterceptor.java index 616115d5e7b..9adecc617dc 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IAnonymousInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IAnonymousInterceptor.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 * %% diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IInterceptorBroadcaster.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorBroadcaster.java similarity index 53% rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IInterceptorBroadcaster.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorBroadcaster.java index 6204991ad93..fc3d0be2850 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IInterceptorBroadcaster.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorBroadcaster.java @@ -1,17 +1,17 @@ -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 * %% * 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. @@ -23,13 +23,20 @@ package ca.uhn.fhir.jpa.model.interceptor.api; public interface IInterceptorBroadcaster { /** - * Invoke the interceptor methods + * Invoke registered interceptor hook methods for the given Pointcut. + * + * @return Returns false if any of the invoked hook methods returned + * false, and returns true otherwise. */ boolean callHooks(Pointcut thePointcut, HookParams theParams); /** - * Invoke the interceptor methods + * Invoke registered interceptor hook methods for the given Pointcut. This method + * should only be called for pointcuts that return a type other than + * void or boolean + * + * @return Returns the object returned by the first hook method that did not return null */ - boolean callHooks(Pointcut thePointcut, Object... theParams); + Object callHooksAndReturnObject(Pointcut thePointcut, HookParams theParams); } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IInterceptorRegistry.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorService.java similarity index 73% rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IInterceptorRegistry.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorService.java index a610e844ae6..581b75afb71 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/IInterceptorRegistry.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IInterceptorService.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 * %% @@ -20,14 +20,11 @@ package ca.uhn.fhir.jpa.model.interceptor.api; * #L% */ -import com.google.common.annotations.VisibleForTesting; - import javax.annotation.Nullable; import java.util.Collection; +import java.util.List; -public interface IInterceptorRegistry { - - int DEFAULT_ORDER = 0; +public interface IInterceptorService extends IInterceptorBroadcaster { /** * Register an interceptor that will be used in a {@link ThreadLocal} context. @@ -35,10 +32,15 @@ public interface IInterceptorRegistry { * they were fired from the current thread. *

* Note that it is almost always desirable to call this method with a - * try-finally statment that removes the interceptor afterwards, since + * try-finally statement that removes the interceptor afterwards, since * this can lead to memory leakage, poor performance due to ever-increasing * numbers of interceptors, etc. *

+ *

+ * Note that most methods such as {@link #getAllRegisteredInterceptors()} and + * {@link #unregisterAllInterceptors()} do not affect thread local interceptors + * as they are kept in a separate list. + *

* * @param theInterceptor The interceptor * @return Returns true if at least one valid hook method was found on this interceptor @@ -68,24 +70,23 @@ public interface IInterceptorRegistry { */ void unregisterInterceptor(Object theInterceptor); - /** - * @deprecated to be removed - */ - @Deprecated - boolean registerGlobalInterceptor(Object theInterceptor); - - /** - * @deprecated to be removed - */ - @Deprecated - void unregisterGlobalInterceptor(Object theInterceptor); - void registerAnonymousInterceptor(Pointcut thePointcut, IAnonymousInterceptor theInterceptor); void registerAnonymousInterceptor(Pointcut thePointcut, int theOrder, IAnonymousInterceptor theInterceptor); - @VisibleForTesting - void clearAnonymousHookForUnitTest(); + /** + * Returns all currently registered interceptors (excluding any thread local interceptors). + */ + List getAllRegisteredInterceptors(); + + /** + * Unregisters all registered interceptors. Note that this method does not unregister + * any {@link #registerThreadLocalInterceptor(Object) thread local interceptors}. + */ + void unregisterAllInterceptors(); void unregisterInterceptors(@Nullable Collection theInterceptors); + + void registerInterceptors(@Nullable Collection theInterceptors); + } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Interceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Interceptor.java similarity index 75% rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Interceptor.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Interceptor.java index 27015c25eae..9ce2d9fe50a 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Interceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Interceptor.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 * %% @@ -33,9 +33,13 @@ import java.lang.annotation.Target; public @interface Interceptor { /** - * @return Declares that an interceptor should be manually registered with the registry, - * and should not auto-register using Spring autowiring. + * @see #order() */ - boolean manualRegistration() default false; + int DEFAULT_ORDER = 0; + /** + * 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. + */ + int order() default DEFAULT_ORDER; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java new file mode 100644 index 00000000000..feaa88c9bfc --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -0,0 +1,1058 @@ +package ca.uhn.fhir.interceptor.api; + +/*- + * #%L + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; + +import javax.annotation.Nonnull; +import java.util.*; + +/** + * Value for {@link Hook#value()} + *

+ * Hook pointcuts are divided into several broad categories: + *

    + *
  • INTERCEPTOR_xxx: Hooks on the interceptor infrastructure itself
  • + *
  • CLIENT_xxx: Hooks on the HAPI FHIR Client framework
  • + *
  • SERVER_xxx: Hooks on the HAPI FHIR Server framework
  • + *
  • SUBSCRIPTION_xxx: Hooks on the HAPI FHIR Subscription framework
  • + *
  • STORAGE_xxx: Hooks on the storage engine
  • + *
  • JPA_PERFTRACE_xxx: Performance tracing hooks on the JPA server
  • + *
+ *

+ */ +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: + *
    + *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • + *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • + *
  • ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchResult
  • + *
+ *

+ * 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: + *
    + *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • + *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • + *
+ *

+ * 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: + *

+ *
    + *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • + *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • + *
+ *

+ * Hooks should return void. + *

+ */ + 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: + *
    + *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • + *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • + *
+ *

+ * Hooks should return void. + *

+ */ + 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: + *
    + *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • + *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • + *
+ *

+ * 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: + *
    + *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • + *
+ *

+ * Hooks should return void. + *

+ */ + 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 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 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 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("
DIVBBB
"); patient.addName().addGiven("NAMEBBB"); myPatientDao.update(patient, mySrd); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index 33aa6cea896..5b8e1c5333a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -426,58 +426,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } } - @Test - public void testCreateWithIfNoneExistBasic() { - String methodName = "testCreateWithIfNoneExistBasic"; - MethodOutcome results; - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id = myPatientDao.create(p, mySrd).getId(); - ourLog.info("Created patient, got it: {}", id); - - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - - reset(myInterceptor); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().addFamily("Hello"); - results = myPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName, mySrd); - assertEquals(id.getIdPart(), results.getId().getIdPart()); - assertFalse(results.getCreated().booleanValue()); - - verifyNoMoreInteractions(myInterceptor); - - // Now create a second one - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().addFamily("Hello"); - results = myPatientDao.create(p, mySrd); - assertNotEquals(id.getIdPart(), results.getId().getIdPart()); - assertTrue(results.getCreated().booleanValue()); - - // Now try to create one with the original match URL and it should fail - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().addFamily("Hello"); - try { - myPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName, mySrd); - fail(); - } catch (PreconditionFailedException e) { - assertThat(e.getMessage(), containsString("Failed to CREATE")); - } - - } - @Test public void testCreateWithIllegalReference() { Observation o1 = new Observation(); @@ -1830,44 +1778,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } - @Test - public void testRead() { - Observation o1 = new Observation(); - o1.getCode().addCoding().setSystem("foo").setCode("testRead"); - IIdType id1 = myObservationDao.create(o1, mySrd).getId(); - - /* - * READ - */ - - reset(myInterceptor); - Observation obs = myObservationDao.read(id1.toUnqualifiedVersionless(), mySrd); - assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode()); - - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals(id1.toUnqualifiedVersionless().getValue(), details.getId().toUnqualifiedVersionless().getValue()); - assertEquals("Observation", details.getResourceType()); - - /* - * VREAD - */ - assertTrue(id1.hasVersionIdPart()); // just to make sure.. - reset(myInterceptor); - obs = myObservationDao.read(id1, mySrd); - assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode()); - - // Verify interceptor - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(id1.toUnqualified().getValue(), details.getId().toUnqualified().getValue()); - assertEquals("Observation", details.getResourceType()); - - } - @Test public void testReadForcedIdVersionHistory() throws InterruptedException { Patient p1 = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java index 443ccf7a519..f6e1b818f01 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java @@ -38,77 +38,6 @@ public class FhirResourceDaoDstu2UpdateTest extends BaseJpaDstu2Test { } - @Test - public void testUpdateAndGetHistoryResource() throws InterruptedException { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("001"); - patient.addName().addFamily("Tester").addGiven("Joe"); - - MethodOutcome outcome = myPatientDao.create(patient, mySrd); - assertNotNull(outcome.getId()); - assertFalse(outcome.getId().isEmpty()); - - assertEquals("1", outcome.getId().getVersionIdPart()); - - Date now = new Date(); - Patient retrieved = myPatientDao.read(outcome.getId(), mySrd); - InstantDt published = (InstantDt) retrieved.getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED); - InstantDt updated = (InstantDt) retrieved.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); - assertTrue(published.before(now)); - assertTrue(updated.before(now)); - - Thread.sleep(1000); - - reset(myInterceptor); - retrieved.getIdentifierFirstRep().setValue("002"); - MethodOutcome outcome2 = myPatientDao.update(retrieved, mySrd); - assertEquals(outcome.getId().getIdPart(), outcome2.getId().getIdPart()); - assertNotEquals(outcome.getId().getVersionIdPart(), outcome2.getId().getVersionIdPart()); - assertEquals("2", outcome2.getId().getVersionIdPart()); - - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertNotNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - - Date now2 = new Date(); - - Patient retrieved2 = myPatientDao.read(outcome.getId().toVersionless(), mySrd); - - assertEquals("2", retrieved2.getId().getVersionIdPart()); - assertEquals("002", retrieved2.getIdentifierFirstRep().getValue()); - InstantDt published2 = (InstantDt) retrieved2.getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED); - InstantDt updated2 = (InstantDt) retrieved2.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); - assertTrue(published2.before(now)); - assertTrue(updated2.after(now)); - assertTrue(updated2.before(now2)); - - Thread.sleep(2000); - - /* - * Get history - */ - - IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, null, mySrd); - - assertEquals(2, historyBundle.size().intValue()); - - List history = historyBundle.getResources(0, 2); - assertEquals("1", history.get(1).getIdElement().getVersionIdPart()); - assertEquals("2", history.get(0).getIdElement().getVersionIdPart()); - assertEquals(published, ResourceMetadataKeyEnum.PUBLISHED.get((IResource) history.get(1))); - assertEquals(published, ResourceMetadataKeyEnum.PUBLISHED.get((IResource) history.get(1))); - assertEquals(updated, ResourceMetadataKeyEnum.UPDATED.get((IResource) history.get(1))); - assertEquals("001", ((Patient) history.get(1)).getIdentifierFirstRep().getValue()); - assertEquals(published2, ResourceMetadataKeyEnum.PUBLISHED.get((IResource) history.get(0))); - assertEquals(updated2, ResourceMetadataKeyEnum.UPDATED.get((IResource) history.get(0))); - assertEquals("002", ((Patient) history.get(0)).getIdentifierFirstRep().getValue()); - - } - @Test public void testUpdateByUrl() { String methodName = "testUpdateByUrl"; @@ -184,7 +113,7 @@ public class FhirResourceDaoDstu2UpdateTest extends BaseJpaDstu2Test { } { Patient p1 = new Patient(); - p1.setId(p1id); + p1.setId(p1id.getValue()); p1.addName().addFamily(methodName); TagList tagList = new TagList(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index 8a6d47a1a38..68b33beb560 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -401,42 +401,6 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { assertEquals(new IdDt(patientId).toUnqualifiedVersionless(), o.getSubject().getReference()); } - @Test - public void testTransactionCreateNoMatchUrl() { - String methodName = "testTransactionCreateNoMatchUrl"; - Bundle request = new Bundle(); - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.setId("Patient/" + methodName); - request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName); - - Bundle resp = mySystemDao.transaction(mySrd, request); - assertEquals(1, resp.getEntry().size()); - - Entry respEntry = resp.getEntry().get(0); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - String patientId = respEntry.getResponse().getLocation(); - assertThat(patientId, not(containsString("test"))); - - /* - * Interceptor should have been called once for the transaction, and once for the embedded operation - */ - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.TRANSACTION), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Bundle", details.getResourceType()); - assertEquals(Bundle.class, details.getResource().getClass()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - - } - @Test public void testTransactionCreateWithDuplicateMatchUrl01() { String methodName = "testTransactionCreateWithDuplicateMatchUrl01"; @@ -1182,76 +1146,6 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { assertThat(respGetBundle.getLink("self").getUrl(), endsWith("/Patient?identifier=testTransactionOrdering")); } - @Test - public void testTransactionReadAndSearch() { - String methodName = "testTransactionReadAndSearch"; - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.setId("Patient/" + methodName); - IIdType idv1 = myPatientDao.update(p, mySrd).getId(); - ourLog.info("Created patient, got id: {}", idv1); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().addFamily("Family Name"); - p.setId("Patient/" + methodName); - IIdType idv2 = myPatientDao.update(p, mySrd).getId(); - ourLog.info("Updated patient, got id: {}", idv2); - - Bundle request = new Bundle(); - request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl(idv1.toUnqualifiedVersionless().getValue()); - request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl(idv1.toUnqualified().getValue()); - request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName); - - Bundle resp = mySystemDao.transaction(mySrd, request); - - assertEquals(3, resp.getEntry().size()); - - Entry nextEntry; - - nextEntry = resp.getEntry().get(0); - assertEquals(Patient.class, nextEntry.getResource().getClass()); - assertEquals(idv2.toUnqualified(), nextEntry.getResource().getId().toUnqualified()); - - nextEntry = resp.getEntry().get(1); - assertEquals(Patient.class, nextEntry.getResource().getClass()); - assertEquals(idv1.toUnqualified(), nextEntry.getResource().getId().toUnqualified()); - - nextEntry = resp.getEntry().get(2); - assertEquals(Bundle.class, nextEntry.getResource().getClass()); - Bundle respBundle = (Bundle) nextEntry.getResource(); - assertEquals(1, respBundle.getTotal().intValue()); - - /* - * Interceptor should have been called once for the transaction, and once for the embedded operation - */ - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.TRANSACTION), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Bundle", details.getResourceType()); - assertEquals(Bundle.class, details.getResource().getClass()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(idv1.toUnqualifiedVersionless(), details.getId()); - assertEquals("Patient", details.getResourceType()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(idv1.toUnqualified(), details.getId()); - assertEquals("Patient", details.getResourceType()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.SEARCH_TYPE), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals("Patient", details.getResourceType()); - - - } - @Test public void testTransactionReadWithIfNoneMatch() { String methodName = "testTransactionReadWithIfNoneMatch"; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java index e6b73837109..259a0e17006 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java @@ -1,22 +1,20 @@ package ca.uhn.fhir.jpa.dao.dstu3; -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.dstu3.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.dstu3.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 BaseJpaDstu3SystemTest extends BaseJpaDstu3Test { protected ServletRequestDetails mySrd; @@ -32,7 +30,7 @@ public abstract class BaseJpaDstu3SystemTest extends BaseJpaDstu3Test { @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/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java index c01ecfb9d17..f82016be338 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java @@ -16,9 +16,11 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; +import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader; import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.util.ResourceCountCache; +import ca.uhn.fhir.jpa.util.ResourceProviderFactory; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; @@ -137,7 +139,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { @Autowired @Qualifier("myImmunizationRecommendationDaoDstu3") protected IFhirResourceDao myImmunizationRecommendationDao; - protected IServerInterceptor myInterceptor; @Autowired @Qualifier("myLocationDaoDstu3") protected IFhirResourceDao myLocationDao; @@ -196,7 +197,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { protected IFhirResourceDao myQuestionnaireResponseDao; @Autowired @Qualifier("myResourceProvidersDstu3") - protected Object myResourceProviders; + protected ResourceProviderFactory myResourceProviders; @Autowired protected IResourceIndexedSearchParamStringDao myResourceIndexedSearchParamStringDao; @Autowired @@ -263,7 +264,11 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { myDaoConfig.setExpireSearchResultsAfterMillis(new DaoConfig().getExpireSearchResultsAfterMillis()); myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); myDaoConfig.setSuppressUpdatesWithNoChange(new DaoConfig().isSuppressUpdatesWithNoChange()); - myDaoConfig.getInterceptors().clear(); + } + + @After + public void afterResetInterceptors() { + myInterceptorRegistry.unregisterAllInterceptors(); } @After @@ -282,12 +287,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { ourJpaValidationSupportChainDstu3 = myJpaValidationSupportChainDstu3; } - @Before - public void beforeCreateInterceptor() { - myInterceptor = mock(IServerInterceptor.class); - myDaoConfig.getInterceptors().add(myInterceptor); - } - @Before public void beforeFlushFT() { runInTransaction(() -> { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3InterceptorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3InterceptorTest.java deleted file mode 100644 index 69f72cf4004..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3InterceptorTest.java +++ /dev/null @@ -1,488 +0,0 @@ -package ca.uhn.fhir.jpa.dao.dstu3; - -import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.dao.DeleteMethodOutcome; -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.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.Bundle.BundleType; -import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.Patient; -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.Mockito; -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 FhirResourceDaoDstu3InterceptorTest extends BaseJpaDstu3Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3InterceptorTest.class); - private IServerOperationInterceptor myJpaInterceptor; - private ServerOperationInterceptorAdapter myJpaInterceptorAdapter = new ServerOperationInterceptorAdapter(); - private IServerOperationInterceptor myServerOperationInterceptor; - - @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); - - myServerOperationInterceptor = mock(IServerOperationInterceptor.class, new Answer() { - @Override - public Object answer(InvocationOnMock theInvocation) throws Throwable { - if (theInvocation.getMethod().getReturnType().equals(boolean.class)) { - return true; - } - return null; - } - }); - - myDaoConfig.getInterceptors().add(myJpaInterceptor); - myDaoConfig.getInterceptors().add(myJpaInterceptorAdapter); - myDaoConfig.getInterceptors().add(myServerOperationInterceptor); - } - - @Test - public void testJpaCreate() { - Patient p = new Patient(); - p.addName().setFamily("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().getIdPartAsLong()); - 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().setFamily("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()); - - } - - /* - * ***************************************************** - * Note that non JPA specific operations get tested in individual - * operation test methods too - * ***************************************************** - */ - - @Test - public void testJpaDelete() { - Patient p = new Patient(); - p.addName().setFamily("PATIENT"); - Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); - - myPatientDao.delete(new IdType("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().getIdPartAsLong()); - assertEquals(id, tableCapt.getValue().getIdElement().getIdPartAsLong()); - - } - - @Test - public void testJpaUpdate() { - Patient p = new Patient(); - p.addName().setFamily("PATIENT"); - Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); - - p = new Patient(); - p.setId(new IdType(id)); - p.addName().setFamily("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().getIdPartAsLong()); - assertEquals(id, tableCapt.getValue().getIdElement().getIdPartAsLong()); - - /* - * Now do a conditional update - */ - - p = new Patient(); - p.setId(new IdType(id)); - p.addName().setFamily("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().setFamily("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().setFamily("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 testServerOperationCreate() { - verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - - Patient p = new Patient(); - p.addName().setFamily("PATIENT"); - IIdType id = myPatientDao.create(p, (RequestDetails)null).getId(); - assertEquals(1L, id.getVersionIdPartAsLong().longValue()); - - verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - } - - @SuppressWarnings("deprecation") - @Test - public void testServerOperationUpdate() { - verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); - - Patient p = new Patient(); - p.addName().setFamily("PATIENT"); - IIdType id = myPatientDao.create(p, (RequestDetails)null).getId(); - assertEquals(1L, id.getVersionIdPartAsLong().longValue()); - - p.addName().setFamily("2"); - myPatientDao.update(p); - - verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); - } - - @Test - public void testServerOperationDelete() { - verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(0)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - - Patient p = new Patient(); - p.addName().setFamily("PATIENT"); - IIdType id = myPatientDao.create(p, (RequestDetails)null).getId(); - assertEquals(1L, id.getVersionIdPartAsLong().longValue()); - - p.addName().setFamily("2"); - myPatientDao.delete(p.getIdElement().toUnqualifiedVersionless()); - - verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(1)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - } - - @Test - public void testRequestOperationDelete() { - Patient p = new Patient(); - p.addName().setFamily("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 IdType("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().setFamily("PATIENT"); - Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); - - p = new Patient(); - p.addName().setFamily("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().setFamily("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(BundleType.TRANSACTION); - xactBundle - .addEntry() - .setResource(p) - .getRequest() - .setUrl("Patient") - .setMethod(HTTPVerb.POST); - Bundle resp = mySystemDao.transaction(mySrd, xactBundle); - - IdType newId = new IdType(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().setFamily("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(BundleType.TRANSACTION); - xactBundle - .addEntry() - .getRequest() - .setUrl("Patient/" + id) - .setMethod(HTTPVerb.DELETE); - Bundle resp = mySystemDao.transaction(mySrd, xactBundle); - - IdType newId = new IdType(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().setFamily("PATIENT"); - Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); - - p = new Patient(); - p.addName().setFamily("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(BundleType.TRANSACTION); - xactBundle - .addEntry() - .getRequest() - .setUrl("Patient?name=PATIENT") - .setMethod(HTTPVerb.DELETE); - Bundle resp = mySystemDao.transaction(mySrd, xactBundle); - - String oo = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp); - 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 testRequestOperationTransactionUpdate() { - Patient p = new Patient(); - p.addName().setFamily("PATIENT"); - final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); - - p = new Patient(); - p.setId(new IdType("Patient/" + id)); - p.addName().setFamily("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(BundleType.TRANSACTION); - xactBundle - .addEntry() - .setResource(p) - .getRequest() - .setUrl("Patient/" + id) - .setMethod(HTTPVerb.PUT); - Bundle resp = mySystemDao.transaction(mySrd, xactBundle); - - IdType newId = new IdType(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().setFamily("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()[0]; - assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue()); - 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 IdType("Patient/" + id)); - p.addName().setFamily("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/dstu3/FhirResourceDaoDstu3SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java index d7cd4a94045..cb7466777cf 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java @@ -264,7 +264,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test { */ patient = new Patient(); - patient.setId(pId1); + patient.setId(pId1.getValue()); patient.getText().setDivAsString("
DIVBBB
"); patient.addName().addGiven("NAMEBBB"); myPatientDao.update(patient, mockSrd()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index 7672b6f3bfb..232a2c55dbd 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -675,58 +675,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertNotEquals("ABC", id); } - @Test - public void testCreateWithIfNoneExistBasic() { - String methodName = "testCreateWithIfNoneExistBasic"; - MethodOutcome results; - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id = myPatientDao.create(p, mySrd).getId(); - ourLog.info("Created patient, got it: {}", id); - - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - - reset(myInterceptor); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().setFamily("Hello"); - results = myPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName, mySrd); - assertEquals(id.getIdPart(), results.getId().getIdPart()); - assertFalse(results.getCreated().booleanValue()); - - verifyNoMoreInteractions(myInterceptor); - - // Now create a second one - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().setFamily("Hello"); - results = myPatientDao.create(p, mySrd); - assertNotEquals(id.getIdPart(), results.getId().getIdPart()); - assertTrue(results.getCreated().booleanValue()); - - // Now try to create one with the original match URL and it should fail - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().setFamily("Hello"); - try { - myPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName, mySrd); - fail(); - } catch (PreconditionFailedException e) { - assertThat(e.getMessage(), containsString("Failed to CREATE")); - } - - } - @Test public void testCreateWithIfNoneExistId() { String methodName = "testCreateWithIfNoneExistId"; @@ -2278,43 +2226,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } - @Test - public void testRead() { - Observation o1 = new Observation(); - o1.getCode().addCoding().setSystem("foo").setCode("testRead"); - IIdType id1 = myObservationDao.create(o1, mySrd).getId(); - - /* - * READ - */ - - reset(myInterceptor); - Observation obs = myObservationDao.read(id1.toUnqualifiedVersionless(), mySrd); - assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode()); - - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals(id1.toUnqualifiedVersionless().getValue(), details.getId().toUnqualifiedVersionless().getValue()); - assertEquals("Observation", details.getResourceType()); - - /* - * VREAD - */ - assertTrue(id1.hasVersionIdPart()); // just to make sure.. - reset(myInterceptor); - obs = myObservationDao.read(id1, mySrd); - assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode()); - - // Verify interceptor - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(id1.toUnqualified().getValue(), details.getId().toUnqualified().getValue()); - assertEquals("Observation", details.getResourceType()); - - } @Test public void testReadForcedIdVersionHistory() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java index ae9c5f521c9..921d2bd7b2d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java @@ -202,7 +202,7 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test { { Patient patient = new Patient(); - patient.setId(id); + patient.setId(id.getValue()); patient.setActive(true); patient.getMeta().addTag().setSystem("http://foo").setCode("bar").setDisplay("Val1"); patient.getMeta().addTag().setSystem("http://foo").setCode("bar").setDisplay("Val2"); @@ -294,76 +294,7 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test { } - @Test - public void testUpdateAndGetHistoryResource() throws InterruptedException { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("001"); - patient.addName().setFamily("Tester").addGiven("Joe"); - MethodOutcome outcome = myPatientDao.create(patient, mySrd); - assertNotNull(outcome.getId()); - assertFalse(outcome.getId().isEmpty()); - - assertEquals("1", outcome.getId().getVersionIdPart()); - - ca.uhn.fhir.jpa.util.TestUtil.sleepOneClick(); - Date now = new Date(); - Patient retrieved = myPatientDao.read(outcome.getId(), mySrd); - InstantType updated = retrieved.getMeta().getLastUpdatedElement().copy(); - assertTrue(updated.before(now)); - - Thread.sleep(1000); - - reset(myInterceptor); - retrieved.getIdentifier().get(0).setValue("002"); - MethodOutcome outcome2 = myPatientDao.update(retrieved, mySrd); - assertEquals(outcome.getId().getIdPart(), outcome2.getId().getIdPart()); - assertNotEquals(outcome.getId().getVersionIdPart(), outcome2.getId().getVersionIdPart()); - assertEquals("2", outcome2.getId().getVersionIdPart()); - - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertNotNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - - Date now2 = new Date(); - - Patient retrieved2 = myPatientDao.read(outcome.getId().toVersionless(), mySrd); - - assertEquals("2", retrieved2.getIdElement().getVersionIdPart()); - assertEquals("002", retrieved2.getIdentifier().get(0).getValue()); - InstantType updated2 = retrieved2.getMeta().getLastUpdatedElement(); - assertTrue(updated2.after(now)); - assertTrue(updated2.before(now2)); - - Thread.sleep(2000); - - /* - * Get history - */ - - IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, null, mySrd); - - assertEquals(2, historyBundle.size().intValue()); - - List history = historyBundle.getResources(0, 2); - - ourLog.info("updated : {}", updated.getValueAsString()); - ourLog.info(" * Exp : {}", ((Resource) history.get(1)).getMeta().getLastUpdatedElement().getValueAsString()); - ourLog.info("updated2: {}", updated2.getValueAsString()); - ourLog.info(" * Exp : {}", ((Resource) history.get(0)).getMeta().getLastUpdatedElement().getValueAsString()); - - assertEquals("1", history.get(1).getIdElement().getVersionIdPart()); - assertEquals("2", history.get(0).getIdElement().getVersionIdPart()); - assertEquals(updated.getValueAsString(), ((Resource) history.get(1)).getMeta().getLastUpdatedElement().getValueAsString()); - assertEquals("001", ((Patient) history.get(1)).getIdentifier().get(0).getValue()); - assertEquals(updated2.getValueAsString(), ((Resource) history.get(0)).getMeta().getLastUpdatedElement().getValueAsString()); - assertEquals("002", ((Patient) history.get(0)).getIdentifier().get(0).getValue()); - - } @Test public void testUpdateByUrl() { @@ -563,7 +494,7 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test { } { Patient p1 = new Patient(); - p1.setId(p1id); + p1.setId(p1id.getValue()); p1.addName().setFamily(methodName); p1.getMeta().addTag("tag_scheme2", "tag_term2", null); @@ -581,7 +512,7 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test { } assertThat(secListValues, containsInAnyOrder("tag_scheme1|tag_term1", "tag_scheme2|tag_term2")); List secList = p1.getMeta().getSecurity(); - secListValues = new HashSet(); + secListValues = new HashSet<>(); for (Coding next : secList) { secListValues.add(next.getSystemElement().getValue() + "|" + next.getCodeElement().getValue()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index df7b1118d00..da04d0461e2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -878,41 +878,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals(new IdType(patientId).toUnqualifiedVersionless().getValue(), o.getSubject().getReference()); } - @Test - public void testTransactionCreateNoMatchUrl() { - String methodName = "testTransactionCreateNoMatchUrl"; - Bundle request = new Bundle(); - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.setId("Patient/" + methodName); - request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName); - - Bundle resp = mySystemDao.transaction(mySrd, request); - assertEquals(1, resp.getEntry().size()); - - BundleEntryComponent respEntry = resp.getEntry().get(0); - assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus()); - String patientId = respEntry.getResponse().getLocation(); - assertThat(patientId, not(containsString("test"))); - - /* - * Interceptor should have been called once for the transaction, and once for the embedded operation - */ - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.TRANSACTION), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Bundle", details.getResourceType()); - assertEquals(Bundle.class, details.getResource().getClass()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - - } @Test public void testTransactionCreateWithBadRead() { @@ -1920,75 +1885,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals(1, allPatients.size().intValue()); } - @Test - public void testTransactionReadAndSearch() { - String methodName = "testTransactionReadAndSearch"; - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.setId("Patient/" + methodName); - IIdType idv1 = myPatientDao.update(p, mySrd).getId(); - ourLog.info("Created patient, got id: {}", idv1); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - p.addName().setFamily("Family Name"); - p.setId("Patient/" + methodName); - IIdType idv2 = myPatientDao.update(p, mySrd).getId(); - ourLog.info("Updated patient, got id: {}", idv2); - - Bundle request = new Bundle(); - request.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl(idv1.toUnqualifiedVersionless().getValue()); - request.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl(idv1.toUnqualified().getValue()); - request.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName); - - Bundle resp = mySystemDao.transaction(mySrd, request); - - assertEquals(3, resp.getEntry().size()); - - BundleEntryComponent nextEntry; - - nextEntry = resp.getEntry().get(0); - assertEquals(Patient.class, nextEntry.getResource().getClass()); - assertEquals(idv2.toUnqualified(), nextEntry.getResource().getIdElement().toUnqualified()); - - nextEntry = resp.getEntry().get(1); - assertEquals(Patient.class, nextEntry.getResource().getClass()); - assertEquals(idv1.toUnqualified(), nextEntry.getResource().getIdElement().toUnqualified()); - - nextEntry = resp.getEntry().get(2); - assertEquals(Bundle.class, nextEntry.getResource().getClass()); - Bundle respBundle = (Bundle) nextEntry.getResource(); - assertEquals(1, respBundle.getTotal()); - - /* - * Interceptor should have been called once for the transaction, and once for the embedded operation - */ - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.TRANSACTION), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Bundle", details.getResourceType()); - assertEquals(Bundle.class, details.getResource().getClass()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(idv1.toUnqualifiedVersionless().getValue(), details.getId().getValue()); - assertEquals("Patient", details.getResourceType()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(idv1.toUnqualified().getValue(), details.getId().getValue()); - assertEquals("Patient", details.getResourceType()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.SEARCH_TYPE), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals("Patient", details.getResourceType()); - - - } @Test public void testTransactionReadWithIfNoneMatch() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java index f70ad99161f..a06e93756e7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java @@ -1,22 +1,20 @@ package ca.uhn.fhir.jpa.dao.r4; -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.r4.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.r4.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 BaseJpaR4SystemTest extends BaseJpaR4Test { protected ServletRequestDetails mySrd; @@ -32,7 +30,7 @@ public abstract class BaseJpaR4SystemTest extends BaseJpaR4Test { @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/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 190b44a4087..4c9f97d43f0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -9,7 +9,7 @@ import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor; import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry; +import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; @@ -17,15 +17,18 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc; import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry; +import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader; import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry; import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.util.ResourceCountCache; +import ca.uhn.fhir.jpa.util.ResourceProviderFactory; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.BasePagingProvider; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; @@ -55,6 +58,7 @@ import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; import java.io.IOException; import java.io.InputStream; +import java.util.List; import java.util.Map; import static org.junit.Assert.fail; @@ -155,7 +159,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { protected IFhirResourceDao myRiskAssessmentDao; protected IServerInterceptor myInterceptor; @Autowired - protected IInterceptorRegistry myInterceptorRegistry; + protected IInterceptorService myInterceptorRegistry; @Autowired @Qualifier("myLocationDaoR4") protected IFhirResourceDao myLocationDao; @@ -220,7 +224,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { protected IFhirResourceDao myQuestionnaireResponseDao; @Autowired @Qualifier("myResourceProvidersR4") - protected Object myResourceProviders; + protected ResourceProviderFactory myResourceProviders; @Autowired protected IResourceTagDao myResourceTagDao; @Autowired @@ -291,6 +295,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { protected SubscriptionRegistry mySubscriptionRegistry; private PerformanceTracingLoggingInterceptor myPerformanceTracingLoggingInterceptor; + private List mySystemInterceptors; @After() public void afterCleanupDao() { @@ -301,9 +306,13 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { myDaoConfig.setSuppressUpdatesWithNoChange(new DaoConfig().isSuppressUpdatesWithNoChange()); myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); - myInterceptorRegistry.clearAnonymousHookForUnitTest(); + myPagingProvider.setDefaultPageSize(BasePagingProvider.DEFAULT_DEFAULT_PAGE_SIZE); + myPagingProvider.setMaximumPageSize(BasePagingProvider.DEFAULT_MAX_PAGE_SIZE); + } - myInterceptorRegistry.unregisterInterceptor(myPerformanceTracingLoggingInterceptor); + @After + public void afterResetInterceptors() { + myInterceptorRegistry.unregisterAllInterceptors(); } @After @@ -324,8 +333,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { @Before public void beforeCreateInterceptor() { + mySystemInterceptors = myInterceptorRegistry.getAllRegisteredInterceptors(); + myInterceptor = mock(IServerInterceptor.class); - myDaoConfig.setInterceptors(myInterceptor); myPerformanceTracingLoggingInterceptor = new PerformanceTracingLoggingInterceptor(); myInterceptorRegistry.registerInterceptor(myPerformanceTracingLoggingInterceptor); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCreatePlaceholdersR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCreatePlaceholdersR4Test.java index 57553c26da6..02c7e6f5472 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCreatePlaceholdersR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCreatePlaceholdersR4Test.java @@ -2,7 +2,9 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.TestUtil; @@ -152,6 +154,12 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test { myPatientDao.read(new IdType("Patient/999999999999999")); + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add("_id", new TokenParam("999999999999999")); + IBundleProvider outcome = myPatientDao.search(map); + assertEquals(1, outcome.size().intValue()); + assertEquals("Patient/999999999999999", outcome.getResources(0,1).get(0).getIdElement().toUnqualifiedVersionless().getValue()); } @AfterClass diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InterceptorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InterceptorTest.java index 8664331dc2c..a244e6ab7eb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InterceptorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InterceptorTest.java @@ -1,5 +1,8 @@ package ca.uhn.fhir.jpa.dao.r4; +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.interceptor.executor.InterceptorService; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DeleteMethodOutcome; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -28,46 +31,29 @@ import java.util.ArrayList; import java.util.List; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.in; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4InterceptorTest.class); - private IServerOperationInterceptor myJpaInterceptor; - private ServerOperationInterceptorAdapter myJpaInterceptorAdapter = new ServerOperationInterceptorAdapter(); - private IServerOperationInterceptor myServerOperationInterceptor; private List myIds = new ArrayList<>(); @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); - myIds.clear(); - - myServerOperationInterceptor = mock(IServerOperationInterceptor.class, new Answer() { - @Override - public Object answer(InvocationOnMock theInvocation) { - if (theInvocation.getMethod().getReturnType().equals(boolean.class)) { - return true; - } - return null; - } - }); - - myDaoConfig.getInterceptors().add(myJpaInterceptor); - myDaoConfig.getInterceptors().add(myJpaInterceptorAdapter); - myDaoConfig.getInterceptors().add(myServerOperationInterceptor); } @Test public void testJpaCreate() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -77,13 +63,13 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); tableCapt = ArgumentCaptor.forClass(IBaseResource.class); - verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); + verify(interceptor, 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()); + verify(interceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); /* * Not do a conditional create @@ -95,8 +81,8 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { 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()); + verify(interceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); + verify(interceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); } @@ -109,6 +95,9 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { @Test public void testJpaDelete() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -120,7 +109,7 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); tableCapt = ArgumentCaptor.forClass(IBaseResource.class); - verify(myJpaInterceptor, times(1)).resourceDeleted(detailsCapt.capture(), tableCapt.capture()); + verify(interceptor, times(1)).resourceDeleted(detailsCapt.capture(), tableCapt.capture()); assertNotNull(tableCapt.getValue().getIdElement().getIdPart()); assertEquals(id, tableCapt.getValue().getIdElement().getIdPartAsLong()); @@ -128,6 +117,9 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { @Test public void testJpaUpdate() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -143,7 +135,7 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); tableCapt = ArgumentCaptor.forClass(IBaseResource.class); - verify(myJpaInterceptor, times(1)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); + verify(interceptor, times(1)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); assertNotNull(tableCapt.getValue().getIdElement().getIdPart()); assertEquals(id, tableCapt.getValue().getIdElement().getIdPartAsLong()); @@ -159,8 +151,8 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { 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()); + verify(interceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); + verify(interceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); assertEquals(id, tableCapt.getAllValues().get(2).getIdElement().getIdPartAsLong()); /* @@ -174,8 +166,8 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { 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()); + verify(interceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture()); + verify(interceptor, times(2)).resourceCreated(detailsCapt.capture(), tableCapt.capture()); assertEquals(id2, tableCapt.getAllValues().get(3).getIdElement().getIdPartAsLong()); } @@ -183,56 +175,55 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { @Test public void testRequestOperationCreate() { IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); - myServerInterceptorList.add(interceptor); + myInterceptorRegistry.registerInterceptor(interceptor); - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock theInvocation) { - 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)); + doAnswer(t -> { + IBaseResource res = (IBaseResource) t.getArguments()[1]; + Long id = res.getIdElement().getIdPartAsLong(); + assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue()); + return null; + }).when(interceptor).resourceCreated(any(), any()); Patient p = new Patient(); p.addName().setFamily("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); + verify(interceptor, times(1)).resourcePreCreate(any(), any()); + verify(interceptor, times(1)).resourceCreated(any(), any()); } @Test public void testRequestOperationDelete() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock theInvocation) { - 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)); + doAnswer(t -> { + IBaseResource res = (IBaseResource) t.getArguments()[1]; + Long id1 = res.getIdElement().getIdPartAsLong(); + assertEquals("Patient/" + id1 + "/_history/2", res.getIdElement().getValue()); + return null; + }).when(interceptor).resourceDeleted(any(), any()); IIdType newId = myPatientDao.delete(new IdType("Patient/" + id), mySrd).getId(); assertEquals(2L, newId.getVersionIdPartAsLong().longValue()); - verify(myRequestOperationCallback, times(1)).resourcePreDelete(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); - verifyNoMoreInteractions(myRequestOperationCallback); + verify(interceptor, times(1)).resourcePreDelete(any(), any()); + verify(interceptor, times(1)).resourcePreCreate(any(), any()); + verify(interceptor, times(1)).resourceDeleted(any(), any()); + verify(interceptor, times(1)).resourceCreated(any(), any()); + verifyNoMoreInteractions(interceptor); } @Test public void testRequestOperationDeleteMulti() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + myDaoConfig.setAllowMultipleDelete(true); Patient p = new Patient(); @@ -246,39 +237,42 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { doAnswer(new Answer() { @Override public Void answer(InvocationOnMock theInvocation) { - IBaseResource res = (IBaseResource) theInvocation.getArguments()[0]; + IBaseResource res = (IBaseResource) theInvocation.getArguments()[1]; Long id = res.getIdElement().getIdPartAsLong(); assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue()); return null; } - }).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class)); + }).when(interceptor).resourceDeleted(any(), any()); 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)).resourceDeleted(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(2)).resourcePreDelete(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(2)).resourcePreCreate(any(IBaseResource.class)); - verifyNoMoreInteractions(myRequestOperationCallback); + verify(interceptor, times(2)).resourceDeleted(any(), any()); + verify(interceptor, times(2)).resourceCreated(any(), any()); + verify(interceptor, times(2)).resourcePreDelete(any(), any()); + verify(interceptor, times(2)).resourcePreCreate(any(), any()); + verifyNoMoreInteractions(interceptor); } @Test public void testRequestOperationTransactionCreate() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock theInvocation) { - IBaseResource res = (IBaseResource) theInvocation.getArguments()[0]; + IBaseResource res = (IBaseResource) theInvocation.getArguments()[1]; Long id = res.getIdElement().getIdPartAsLong(); assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue()); return null; } - }).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class)); + }).when(interceptor).resourceCreated(any(), any()); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); @@ -293,13 +287,16 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation()); assertEquals(1L, newId.getVersionIdPartAsLong().longValue()); - verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class)); - verifyNoMoreInteractions(myRequestOperationCallback); + verify(interceptor, times(1)).resourceCreated(any(), any()); + verify(interceptor, times(1)).resourcePreCreate(any(), any()); + verifyNoMoreInteractions(interceptor); } @Test public void testRequestOperationTransactionDelete() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -307,12 +304,12 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { doAnswer(new Answer() { @Override public Void answer(InvocationOnMock theInvocation) { - IBaseResource res = (IBaseResource) theInvocation.getArguments()[0]; + IBaseResource res = (IBaseResource) theInvocation.getArguments()[1]; Long id = res.getIdElement().getIdPartAsLong(); assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue()); return null; } - }).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class)); + }).when(interceptor).resourceDeleted(any(), any()); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); @@ -326,15 +323,18 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation()); assertEquals(2L, newId.getVersionIdPartAsLong().longValue()); - verify(myRequestOperationCallback, times(1)).resourcePreDelete(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); - verifyNoMoreInteractions(myRequestOperationCallback); + verify(interceptor, times(1)).resourcePreDelete(any(), any()); + verify(interceptor, times(1)).resourcePreCreate(any(), any()); + verify(interceptor, times(1)).resourceDeleted(any(), any()); + verify(interceptor, times(1)).resourceCreated(any(), any()); + verifyNoMoreInteractions(interceptor); } @Test public void testRequestOperationTransactionDeleteMulti() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + myDaoConfig.setAllowMultipleDelete(true); Patient p = new Patient(); @@ -348,12 +348,12 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { doAnswer(new Answer() { @Override public Void answer(InvocationOnMock theInvocation) { - IBaseResource res = (IBaseResource) theInvocation.getArguments()[0]; + IBaseResource res = (IBaseResource) theInvocation.getArguments()[1]; Long id = res.getIdElement().getIdPartAsLong(); assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue()); return null; } - }).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class)); + }).when(interceptor).resourceDeleted(any(), any()); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); @@ -368,15 +368,18 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { ourLog.info(oo); assertThat(oo, containsString("deleted 2 resource(s)")); - verify(myRequestOperationCallback, times(2)).resourceDeleted(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(2)).resourcePreDelete(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(2)).resourcePreCreate(any(IBaseResource.class)); - verifyNoMoreInteractions(myRequestOperationCallback); + verify(interceptor, times(2)).resourceDeleted(any(), any()); + verify(interceptor, times(2)).resourceCreated(any(), any()); + verify(interceptor, times(2)).resourcePreDelete(any(), any()); + verify(interceptor, times(2)).resourcePreCreate(any(), any()); + verifyNoMoreInteractions(interceptor); } @Test public void testRequestOperationTransactionUpdate() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -388,11 +391,17 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { doAnswer(new Answer() { @Override public Void answer(InvocationOnMock theInvocation) { + // Old contents IBaseResource res = (IBaseResource) theInvocation.getArguments()[1]; + assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue()); + + // New contents + res = (IBaseResource) theInvocation.getArguments()[2]; assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue()); + return null; } - }).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class)); + }).when(interceptor).resourceUpdated(any(), any(), any()); Bundle xactBundle = new Bundle(); xactBundle.setType(BundleType.TRANSACTION); @@ -407,16 +416,17 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation()); assertEquals(2L, newId.getVersionIdPartAsLong().longValue()); - verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class)); - verifyNoMoreInteractions(myRequestOperationCallback); + verify(interceptor, times(1)).resourceUpdated(any(), any(), any()); + verify(interceptor, times(1)).resourceCreated(any(), any()); + verify(interceptor, times(1)).resourcePreCreate(any(), any()); + verify(interceptor, times(1)).resourcePreUpdate(any(), any(), any()); } @Test public void testRequestOperationUpdate() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + Patient p = new Patient(); p.addName().setFamily("PATIENT"); final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -424,13 +434,13 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { doAnswer(new Answer() { @Override public Void answer(InvocationOnMock theInvocation) { - IBaseResource res = (IBaseResource) theInvocation.getArguments()[0]; + IBaseResource res = (IBaseResource) theInvocation.getArguments()[1]; assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue()); - res = (IBaseResource) theInvocation.getArguments()[1]; + res = (IBaseResource) theInvocation.getArguments()[2]; assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue()); return null; } - }).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class)); + }).when(interceptor).resourceUpdated(any(), any(), any()); p = new Patient(); p.setId(new IdType("Patient/" + id)); @@ -438,30 +448,34 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { IIdType newId = myPatientDao.update(p, mySrd).getId(); assertEquals(2L, newId.getVersionIdPartAsLong().longValue()); - verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class)); - verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class)); - verifyNoMoreInteractions(myRequestOperationCallback); + verify(interceptor, times(1)).resourceUpdated(any(), any(), any()); + verify(interceptor, times(1)).resourceCreated(any(), any()); + verify(interceptor, times(1)).resourcePreCreate(any(), any()); + verify(interceptor, times(1)).resourcePreUpdate(any(), any(), any()); } @Test public void testServerOperationCreate() { - verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + + verify(interceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any()); Patient p = new Patient(); p.addName().setFamily("PATIENT"); IIdType id = myPatientDao.create(p, (RequestDetails) null).getId(); assertEquals(1L, id.getVersionIdPartAsLong().longValue()); - verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); + verify(interceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any()); } @Test public void testServerOperationDelete() { - verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(0)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + + verify(interceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any()); + verify(interceptor, times(0)).resourceDeleted(Mockito.isNull(RequestDetails.class), any()); Patient p = new Patient(); p.addName().setFamily("PATIENT"); @@ -471,67 +485,73 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { p.addName().setFamily("2"); myPatientDao.delete(p.getIdElement().toUnqualifiedVersionless()); - verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(1)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); + verify(interceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any()); + verify(interceptor, times(1)).resourceDeleted(Mockito.isNull(RequestDetails.class), any()); } + /** + * Make sure that both JPA interceptors and RestfulServer interceptors can + * get called + */ @Test - public void testServerOperationInterceptorCanModifyOnCreate() { + public void testServerOperationInterceptorCanModifyOnCreateForJpaInterceptor() { - ServerOperationInterceptorAdapter interceptor = new ServerOperationInterceptorAdapter() { - @Override - public void resourcePreCreate(RequestDetails theRequest, IBaseResource theResource) { + Object interceptor = new Object() { + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED) + public void resourcePreCreate(IBaseResource theResource) { ((Patient) theResource).setActive(true); } }; - myDaoConfig.getInterceptors().add(interceptor); - try { + myRequestOperationCallback.registerInterceptor(interceptor); - doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourcePreCreate(any(RequestDetails.class), any(IBaseResource.class)); - doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourceCreated(any(RequestDetails.class), any(IBaseResource.class)); + Patient p = new Patient(); + p.setActive(false); + IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - Patient p = new Patient(); - p.setActive(false); - IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); + p = myPatientDao.read(id); + assertEquals(true, p.getActive()); - InOrder inorder = inOrder(myJpaInterceptor, myRequestOperationCallback); - inorder.verify(myRequestOperationCallback, times(1)).resourcePreCreate(any(IBaseResource.class)); - inorder.verify(myJpaInterceptor, times(1)).resourcePreCreate(any(RequestDetails.class), any(IBaseResource.class)); - inorder.verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class)); - inorder.verify(myJpaInterceptor, times(1)).resourceCreated(any(RequestDetails.class), any(IBaseResource.class)); + } - assertNull(myIds.get(0).getIdPart()); - assertNull(myIds.get(0).getVersionIdPart()); - assertNotNull(myIds.get(1).getIdPart()); - assertEquals("1", myIds.get(1).getVersionIdPart()); + /** + * Make sure that both JPA interceptors and RestfulServer interceptors can + * get called + */ + @Test + public void testServerOperationInterceptorCanModifyOnCreateForServerInterceptor() { - p = myPatientDao.read(id); - assertEquals(true, p.getActive()); + Object interceptor = new Object() { + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED) + public void resourcePreCreate(IBaseResource theResource) { + ((Patient) theResource).setActive(true); + } + }; + + myInterceptorRegistry.registerInterceptor(interceptor); + + Patient p = new Patient(); + p.setActive(false); + IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); + + p = myPatientDao.read(id); + assertEquals(true, p.getActive()); - } finally { - myDaoConfig.getInterceptors().remove(interceptor); - } } @Test public void testServerOperationInterceptorCanModifyOnUpdate() { - ServerOperationInterceptorAdapter interceptor = new ServerOperationInterceptorAdapter() { - @Override + Object interceptor = new Object() { + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED) public void resourcePreUpdate(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) { ((Patient) theNewResource).setActive(true); } }; - myDaoConfig.getInterceptors().add(interceptor); - try { - - doAnswer(new MyTwoResourceAnswer()).when(myJpaInterceptor).resourcePreUpdate(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); - doAnswer(new MyTwoResourceAnswer()).when(myJpaInterceptor).resourceUpdated(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); + myInterceptorRegistry.registerInterceptor(interceptor); Patient p = new Patient(); p.setActive(false); IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); - String idPart = id.getIdPart(); p = myPatientDao.read(id); assertEquals(false, p.getActive()); @@ -540,36 +560,18 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { p.addAddress().setCity("CITY"); myPatientDao.update(p, mySrd); - InOrder inorder = inOrder(myJpaInterceptor, myRequestOperationCallback); - inorder.verify(myRequestOperationCallback, times(1)).resourcePreUpdate(any(IBaseResource.class), any(IBaseResource.class)); - inorder.verify(myJpaInterceptor, times(1)).resourcePreUpdate(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); - inorder.verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class)); - inorder.verify(myJpaInterceptor, times(1)).resourceUpdated(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); - - // resourcePreUpdate - assertEquals(idPart, myIds.get(0).getIdPart()); - assertEquals("1", myIds.get(0).getVersionIdPart()); - assertEquals(idPart, myIds.get(1).getIdPart()); - assertEquals(null, myIds.get(1).getVersionIdPart()); - // resourceUpdated - assertEquals(idPart, myIds.get(2).getIdPart()); - assertEquals("1", myIds.get(2).getVersionIdPart()); - assertEquals(idPart, myIds.get(3).getIdPart()); - assertEquals("2", myIds.get(3).getVersionIdPart()); - p = myPatientDao.read(id); assertEquals(true, p.getActive()); - } finally { - myDaoConfig.getInterceptors().remove(interceptor); - } } @Test public void testServerOperationPreDelete() { + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); - doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourcePreDelete(nullable(ServletRequestDetails.class), any(Patient.class)); - doAnswer(new MyOneResourceAnswer()).when(myJpaInterceptor).resourceDeleted(nullable(ServletRequestDetails.class), any(Patient.class)); + doAnswer(new MyOneResourceAnswer()).when(interceptor).resourcePreDelete(nullable(ServletRequestDetails.class), any(Patient.class)); + doAnswer(new MyOneResourceAnswer()).when(interceptor).resourceDeleted(nullable(ServletRequestDetails.class), any(Patient.class)); Patient p = new Patient(); p.setActive(false); @@ -578,9 +580,9 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { myPatientDao.delete(id); - InOrder inorder = inOrder(myJpaInterceptor); - inorder.verify(myJpaInterceptor, times(1)).resourcePreDelete(nullable(ServletRequestDetails.class), any(Patient.class)); - inorder.verify(myJpaInterceptor, times(1)).resourceDeleted(nullable(ServletRequestDetails.class), any(Patient.class)); + InOrder inorder = inOrder(interceptor); + inorder.verify(interceptor, times(1)).resourcePreDelete(nullable(ServletRequestDetails.class), any(Patient.class)); + inorder.verify(interceptor, times(1)).resourceDeleted(nullable(ServletRequestDetails.class), any(Patient.class)); // resourcePreDelete assertEquals(idPart, myIds.get(0).getIdPart()); assertEquals("1", myIds.get(0).getVersionIdPart()); @@ -593,9 +595,12 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { @SuppressWarnings("deprecation") @Test public void testServerOperationUpdate() { - verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); + IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class); + myInterceptorRegistry.registerInterceptor(interceptor); + + verify(interceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any()); + verify(interceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any()); + verify(interceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(), any()); Patient p = new Patient(); p.addName().setFamily("PATIENT"); @@ -605,14 +610,9 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { p.addName().setFamily("2"); myPatientDao.update(p); - verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class)); - verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); + verify(interceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any()); + verify(interceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any()); + verify(interceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(), any()); } private class MyOneResourceAnswer implements Answer { @@ -634,4 +634,9 @@ public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test { return null; } } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java index 680ec2b63c7..2188975cc8b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java @@ -277,7 +277,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { */ patient = new Patient(); - patient.setId(pId1); + patient.setId(pId1.getValue()); patient.getText().setDivAsString("
DIVBBB
"); patient.addName().addGiven("NAMEBBB"); myPatientDao.update(patient, mockSrd()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SelectiveUpdateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SelectiveUpdateTest.java index 2b05eed0d7f..daa418a4594 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SelectiveUpdateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SelectiveUpdateTest.java @@ -21,41 +21,37 @@ public class FhirResourceDaoR4SelectiveUpdateTest extends BaseJpaR4Test { @Test public void testInterceptorPreservesAttribute() throws Exception { CentralAttributesPreservationInterceptor interceptor = new CentralAttributesPreservationInterceptor(); - myDaoConfig.getInterceptors().add(interceptor); - try { + myInterceptorRegistry.registerInterceptor(interceptor); - // Create the patient with no additional identifier - Patient p = new Patient(); - p.setActive(true); - p.addIdentifier().setSystem("http://foo").setValue("bar"); - IIdType id = myPatientDao.create(p).getId().toUnqualified(); - assertEquals("1", id.getVersionIdPart()); + // Create the patient with no additional identifier + Patient p = new Patient(); + p.setActive(true); + p.addIdentifier().setSystem("http://foo").setValue("bar"); + IIdType id = myPatientDao.create(p).getId().toUnqualified(); + assertEquals("1", id.getVersionIdPart()); - // Update to add a preserved identifier - p = new Patient(); - p.setId(id.toVersionless()); - p.setActive(true); - p.addIdentifier(new Identifier().setSystem("http://foo").setValue("bar")); - p.addIdentifier(new Identifier().setSystem(EUID_SYSTEM).setValue("123")); - id = myPatientDao.update(p).getId().toUnqualified(); - assertEquals("2", id.getVersionIdPart()); + // Update to add a preserved identifier + p = new Patient(); + p.setId(id.toVersionless()); + p.setActive(true); + p.addIdentifier(new Identifier().setSystem("http://foo").setValue("bar")); + p.addIdentifier(new Identifier().setSystem(EUID_SYSTEM).setValue("123")); + id = myPatientDao.update(p).getId().toUnqualified(); + assertEquals("2", id.getVersionIdPart()); - // Update to change something but include the preserved attribute - p = new Patient(); - p.setId(id.toVersionless()); - p.setActive(false); - p.addIdentifier(new Identifier().setSystem("http://foo").setValue("bar")); - id = myPatientDao.update(p).getId().toUnqualified(); - assertEquals("3", id.getVersionIdPart()); + // Update to change something but include the preserved attribute + p = new Patient(); + p.setId(id.toVersionless()); + p.setActive(false); + p.addIdentifier(new Identifier().setSystem("http://foo").setValue("bar")); + id = myPatientDao.update(p).getId().toUnqualified(); + assertEquals("3", id.getVersionIdPart()); - // Read it back - p = myPatientDao.read(id); - assertEquals(false, p.getActive()); - assertEquals(2, p.getIdentifier().size()); + // Read it back + p = myPatientDao.read(id); + assertEquals(false, p.getActive()); + assertEquals(2, p.getIdentifier().size()); - } finally { - myDaoConfig.getInterceptors().remove(interceptor); - } } public class CentralAttributesPreservationInterceptor extends ServerOperationInterceptorAdapter { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java index 68c3e70202c..2e26893205e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java @@ -76,6 +76,12 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { myDaoConfig.setEnforceReferenceTargetTypes(new DaoConfig().isEnforceReferenceTargetTypes()); } + @Before + public void before() { + myInterceptorRegistry.registerInterceptor(myInterceptor); + } + + private void assertGone(IIdType theId) { try { assertNotGone(theId); @@ -835,24 +841,12 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { IIdType id = myPatientDao.create(p, mySrd).getId(); ourLog.info("Created patient, got it: {}", id); - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - - reset(myInterceptor); - p = new Patient(); p.addIdentifier().setSystem("urn:system").setValue(methodName); p.addName().setFamily("Hello"); results = myPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName, mySrd); assertEquals(id.getIdPart(), results.getId().getIdPart()); - assertFalse(results.getCreated().booleanValue()); - - verifyNoMoreInteractions(myInterceptor); + assertFalse(results.getCreated()); // Now create a second one @@ -1691,7 +1685,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { middleDate = new Date(); Thread.sleep(fullSize); } - patient.setId(id); + patient.setId(id.getValue()); patient.getName().get(0).getFamilyElement().setValue(methodName + "_i" + i); myPatientDao.update(patient, mySrd); } @@ -1921,7 +1915,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { Thread.sleep(100); preDates.add(new Date()); Thread.sleep(100); - patient.setId(id); + patient.setId(id.getValue()); patient.getName().get(0).getFamilyElement().setValue(methodName + "_i" + i); ids.add(myPatientDao.update(patient, mySrd).getId().toUnqualified().getValue()); } @@ -2509,17 +2503,9 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { * READ */ - reset(myInterceptor); Observation obs = myObservationDao.read(id1.toUnqualifiedVersionless(), mySrd); assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode()); - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals(id1.toUnqualifiedVersionless().getValue(), details.getId().toUnqualifiedVersionless().getValue()); - assertEquals("Observation", details.getResourceType()); - /* * VREAD */ @@ -2528,13 +2514,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { obs = myObservationDao.read(id1, mySrd); assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode()); - // Verify interceptor - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(id1.toUnqualified().getValue(), details.getId().toUnqualified().getValue()); - assertEquals("Observation", details.getResourceType()); - } @Test @@ -2611,7 +2590,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { assertGone(id); - patient.setId(id); + patient.setId(id.getValue()); patient.addAddress().addLine("AAA"); myPatientDao.update(patient, mySrd); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java index 6d3a3526a88..aa37d4b9513 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java @@ -662,8 +662,9 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test { assertEquals(3, uniqueSearchParams.get(0).getComponents().size()); myResourceReindexingSvc.markAllResourcesForReindexing(); - myResourceReindexingSvc.forceReindexingPass(); - myResourceReindexingSvc.forceReindexingPass(); + assertEquals(6, myResourceReindexingSvc.forceReindexingPass()); + assertEquals(1, myResourceReindexingSvc.forceReindexingPass()); + assertEquals(0, myResourceReindexingSvc.forceReindexingPass()); List uniques = myResourceIndexedCompositeStringUniqueDao.findAll(); assertEquals(uniques.toString(), 1, uniques.size()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java index 5702eed11f3..1858293f401 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java @@ -18,10 +18,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetai import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.*; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.*; import org.mockito.ArgumentCaptor; import org.springframework.test.context.TestPropertySource; @@ -45,6 +42,11 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields()); } + @Before + public void before() { + myInterceptorRegistry.registerInterceptor(myInterceptor); + } + @Test public void testCreateAndUpdateWithoutRequest() { String methodName = "testUpdateByUrl"; @@ -96,7 +98,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { return myPatientDao.create(p).getId().toUnqualified(); }); - String createTime = runInTransaction(()->{ + String createTime = runInTransaction(() -> { List allResources = myResourceTableDao.findAll(); assertEquals(1, allResources.size()); ResourceTable resourceTable = allResources.get(0); @@ -110,7 +112,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { }); myCaptureQueriesListener.clear(); - runInTransaction(()->{ + runInTransaction(() -> { Patient p = new Patient(); p.setId(id.getIdPart()); p.addIdentifier().setSystem("urn:system").setValue("2"); @@ -124,7 +126,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { assertThat(myCaptureQueriesListener.getInsertQueriesForCurrentThread(), empty()); assertThat(myCaptureQueriesListener.getDeleteQueriesForCurrentThread(), empty()); - runInTransaction(()->{ + runInTransaction(() -> { List allResources = myResourceTableDao.findAll(); assertEquals(1, allResources.size()); ResourceTable resourceTable = allResources.get(0); @@ -191,7 +193,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { } } - + @Test public void testDuplicateTagsOnUpdateIgnored() { IIdType id; @@ -203,7 +205,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { { Patient patient = new Patient(); - patient.setId(id); + patient.setId(id.getValue()); patient.setActive(true); patient.getMeta().addTag().setSystem("http://foo").setCode("bar").setDisplay("Val1"); patient.getMeta().addTag().setSystem("http://foo").setCode("bar").setDisplay("Val2"); @@ -275,7 +277,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { patient.setActive(true); id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); } - + { Meta meta = new Meta(); meta.addTag().setSystem("http://foo").setCode("1"); @@ -291,7 +293,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { } } - + @Test public void testMultipleUpdatesWithNoChangesDoesNotResultInAnUpdateForDiscreteUpdates() { @@ -384,14 +386,6 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { assertNotEquals(outcome.getId().getVersionIdPart(), outcome2.getId().getVersionIdPart()); assertEquals("2", outcome2.getId().getVersionIdPart()); - // Verify interceptor - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertNotNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - TestUtil.sleepOneClick(); Date now2 = new Date(); @@ -627,7 +621,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { } { Patient p1 = new Patient(); - p1.setId(p1id); + p1.setId(p1id.getValue()); p1.addName().setFamily(methodName); p1.getMeta().addTag("tag_scheme2", "tag_term2", null); @@ -645,7 +639,7 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { } assertThat(secListValues, containsInAnyOrder("tag_scheme1|tag_term1", "tag_scheme2|tag_term2")); List secList = p1.getMeta().getSecurity(); - secListValues = new HashSet(); + secListValues = new HashSet<>(); for (Coding next : secList) { secListValues.add(next.getSystemElement().getValue() + "|" + next.getCodeElement().getValue()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index a66f692f6a3..322c4d33c08 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -58,6 +58,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { @Before public void beforeDisableResultReuse() { + myInterceptorRegistry.registerInterceptor(myInterceptor); myDaoConfig.setReuseCachedSearchResultsForMillis(null); } @@ -590,13 +591,13 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { final IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); p = new Patient(); - p.setId(id); + p.setId(id.getValue()); p.addName().setFamily("family1"); p.addName().setFamily("family2"); myPatientDao.update(p); p = new Patient(); - p.setId(id); + p.setId(id.getValue()); p.addName().setFamily("family1"); p.addName().setFamily("family2"); p.addName().setFamily("family3"); @@ -1308,22 +1309,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { String patientId = respEntry.getResponse().getLocation(); assertThat(patientId, not(containsString("test"))); - /* - * Interceptor should have been called once for the transaction, and once for the embedded operation - */ - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.TRANSACTION), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Bundle", details.getResourceType()); - assertEquals(Bundle.class, details.getResource().getClass()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertNull(details.getId()); - assertEquals("Patient", details.getResourceType()); - assertEquals(Patient.class, details.getResource().getClass()); - } @Test @@ -2374,33 +2359,6 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { Bundle respBundle = (Bundle) nextEntry.getResource(); assertEquals(1, respBundle.getTotal()); - /* - * Interceptor should have been called once for the transaction, and once for the embedded operation - */ - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.TRANSACTION), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Bundle", details.getResourceType()); - assertEquals(Bundle.class, details.getResource().getClass()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(idv1.toUnqualifiedVersionless().getValue(), details.getId().getValue()); - assertEquals("Patient", details.getResourceType()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals(idv1.toUnqualified().getValue(), details.getId().getValue()); - assertEquals("Patient", details.getResourceType()); - - detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - verify(myInterceptor, times(1)).incomingRequestPreHandled(eq(RestOperationTypeEnum.SEARCH_TYPE), detailsCapt.capture()); - details = detailsCapt.getValue(); - assertEquals("Patient", details.getResourceType()); - - } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java index 28d6be346d9..5006062b507 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java @@ -72,7 +72,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test { ourServerBase = "http://localhost:" + ourPort + "/fhir/context"; - ourRestServer.setResourceProviders((List)myResourceProviders); + ourRestServer.registerProviders(myResourceProviders.createProviders()); ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderInterceptorDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderInterceptorDstu2Test.java deleted file mode 100644 index 61fd1f7a803..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderInterceptorDstu2Test.java +++ /dev/null @@ -1,192 +0,0 @@ -package ca.uhn.fhir.jpa.provider; - -import ca.uhn.fhir.jpa.provider.r4.ResourceProviderInterceptorR4Test; -import ca.uhn.fhir.model.dstu2.resource.Bundle; -import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; -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.rest.api.Constants; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.server.ResponseDetails; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -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.interceptor.InterceptorAdapter; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -@SuppressWarnings("deprecation") -public class ResourceProviderInterceptorDstu2Test extends BaseResourceProviderDstu2Test { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderInterceptorDstu2Test.class); - private IServerInterceptor myDaoInterceptor; - - private IServerInterceptor myServerInterceptor; - private IServerOperationInterceptor myJpaServerInterceptor; - - @Override - @After - public void after() throws Exception { - super.after(); - - myDaoConfig.getInterceptors().remove(myDaoInterceptor); - ourRestServer.unregisterInterceptor(myServerInterceptor); - ourRestServer.unregisterInterceptor(myJpaServerInterceptor); - } - - @Override - public void before() throws Exception { - super.before(); - - myServerInterceptor = mock(IServerInterceptor.class); - myDaoInterceptor = mock(IServerInterceptor.class); - myJpaServerInterceptor = mock(IServerOperationInterceptor.class); - - when(myServerInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true); - when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - - when(myJpaServerInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myJpaServerInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myJpaServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myJpaServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true); - when(myJpaServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myJpaServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - - myDaoConfig.getInterceptors().add(myDaoInterceptor); - ourRestServer.registerInterceptor(myServerInterceptor); - ourRestServer.registerInterceptor(myJpaServerInterceptor); - - ourRestServer.registerInterceptor(new InterceptorAdapter() { - @Override - public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) { - super.incomingRequestPreHandled(theOperation, theProcessedRequest); - } - }); - - } - - @Test - public void testCreateResource() throws IOException { - String methodName = "testCreateResource"; - - Patient pt = new Patient(); - pt.addName().addFamily(methodName); - String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt); - - HttpPost post = new HttpPost(ourServerBase + "/Patient"); - post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - try (CloseableHttpResponse response = ourHttpClient.execute(post)) { - String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was: {}", resp); - assertEquals(201, response.getStatusLine().getStatusCode()); - String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); - assertThat(newIdString, startsWith(ourServerBase + "/Patient/")); - } - - ArgumentCaptor ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - ArgumentCaptor opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myServerInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getValue()); - assertEquals("Patient", ardCaptor.getValue().getResourceType()); - assertNotNull(ardCaptor.getValue().getResource()); - - ResourceProviderInterceptorR4Test.verifyDaoInterceptor(myDaoInterceptor); - } - - @Test - public void testCreateResourceInTransaction() throws IOException { - String methodName = "testCreateResourceInTransaction"; - - Patient pt = new Patient(); - pt.addName().addFamily(methodName); - - Bundle bundle = new Bundle(); - bundle.setType(BundleTypeEnum.TRANSACTION); - Entry entry = bundle.addEntry(); - entry.setFullUrl("Patient"); - entry.setResource(pt); - entry.getRequest().setMethod(HTTPVerbEnum.POST); - entry.getRequest().setUrl("Patient"); - - String resource = myFhirCtx.newXmlParser().encodeResourceToString(bundle); - - HttpPost post = new HttpPost(ourServerBase + "/"); - post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - try (CloseableHttpResponse response = ourHttpClient.execute(post)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - } - - /* - * Server Interceptor - */ - - ArgumentCaptor ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - ArgumentCaptor opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myServerInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals("Had types: " + opTypeCaptor.getAllValues() + " and requests: " + ardCaptor.getAllValues(), RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0)); - assertNull(ardCaptor.getAllValues().get(0).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(0).getResource()); - assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1)); - assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(1).getResource()); - - ArgumentCaptor rdCaptor = ArgumentCaptor.forClass(RequestDetails.class); - ArgumentCaptor srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); - ArgumentCaptor sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class); - verify(myServerInterceptor, times(1)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture()); - - verify(myJpaServerInterceptor, times(1)).resourceCreated(ArgumentCaptor.forClass(RequestDetails.class).capture(), ArgumentCaptor.forClass(IBaseResource.class).capture()); - - /* - * DAO Interceptor - */ - - ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myDaoInterceptor, atLeast(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals("Had types: " + opTypeCaptor.getAllValues() + " and requests: " + ardCaptor.getAllValues(), RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0)); - assertEquals("Bundle", ardCaptor.getAllValues().get(0).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(0).getResource()); - assertEquals("Had types: " + opTypeCaptor.getAllValues() + " and requests: " + ardCaptor.getAllValues(), RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1)); - assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(1).getResource()); - - rdCaptor = ArgumentCaptor.forClass(RequestDetails.class); - srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); - sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class); - verify(myDaoInterceptor, times(0)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture()); - - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java index e7020470a33..a8898f2417d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java @@ -13,6 +13,7 @@ import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; 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; @@ -21,19 +22,24 @@ import java.util.ArrayList; import java.util.List; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.in; import static org.junit.Assert.*; public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionsDstu2Test.class); + private SubscriptionsRequireManualActivationInterceptorDstu2 myInterceptor; - @Override + @Before public void beforeCreateInterceptor() { - super.beforeCreateInterceptor(); + myInterceptor = new SubscriptionsRequireManualActivationInterceptorDstu2(); + myInterceptor.setDao(mySubscriptionDao); + myInterceptorRegistry.registerInterceptor(myInterceptor); + } - SubscriptionsRequireManualActivationInterceptorDstu2 interceptor = new SubscriptionsRequireManualActivationInterceptorDstu2(); - interceptor.setDao(mySubscriptionDao); - myDaoConfig.getInterceptors().add(interceptor); + @After + public void afterDestroyInterceptor() { + myInterceptorRegistry.unregisterInterceptor(myInterceptor); } @Before diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java deleted file mode 100644 index b0c365e905f..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java +++ /dev/null @@ -1,367 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; -import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; -import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; -import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; -import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; -import ca.uhn.fhir.util.TestUtil; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.Observation; -import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; -import org.hl7.fhir.dstu3.model.Patient; -import org.hl7.fhir.dstu3.model.Practitioner; -import org.hl7.fhir.dstu3.model.Reference; -import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.AfterClass; -import org.junit.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.*; - -public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { - - @Override - public void before() throws Exception { - super.before(); - myDaoConfig.setAllowMultipleDelete(true); - unregisterInterceptors(); - } - - /** - * See #778 - */ - @Test - public void testReadingObservationAccessRight() { - Practitioner practitioner1 = new Practitioner(); - final IIdType practitionerId1 = ourClient.create().resource(practitioner1).execute().getId().toUnqualifiedVersionless(); - - Practitioner practitioner2 = new Practitioner(); - final IIdType practitionerId2 = ourClient.create().resource(practitioner2).execute().getId().toUnqualifiedVersionless(); - - Patient patient = new Patient(); - patient.setActive(true); - final IIdType patientId = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless(); - - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - // allow write all Observation resource - // allow read only Observation resource in which it has a practitioner1 or practitioner2 compartment - return new RuleBuilder().allow() - .write() - .resourcesOfType(Observation.class) - .withAnyId() - .andThen() - .allow() - .read() - .resourcesOfType(Observation.class) - .inCompartment("Practitioner", Arrays.asList(practitionerId1, practitionerId2)) - .andThen() - .denyAll() - .build(); - } - }); - - Observation obs1 = new Observation(); - obs1.setStatus(ObservationStatus.FINAL); - obs1.setPerformer( - Arrays.asList(new Reference(practitionerId1), new Reference(practitionerId2))); - IIdType oid1 = ourClient.create().resource(obs1).execute().getId().toUnqualified(); - - // Observation with practitioner1 and practitioner1 as the Performer -> should have the read access - ourClient.read().resource(Observation.class).withId(oid1).execute(); - - Observation obs2 = new Observation(); - obs2.setStatus(ObservationStatus.FINAL); - obs2.setSubject(new Reference(patientId)); - IIdType oid2 = ourClient.create().resource(obs2).execute().getId().toUnqualified(); - - // Observation with patient as the subject -> read access should be blocked - try { - ourClient.read().resource(Observation.class).withId(oid2).execute(); - fail(); - } catch (ForbiddenOperationException e) { - // good - } - } - - /** - * See #667 - */ - @Test - public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() { - Patient pt1 = new Patient(); - pt1.setActive(true); - final IIdType pid1 = ourClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless(); - - Patient pt2 = new Patient(); - pt2.setActive(false); - final IIdType pid2 = ourClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless(); - - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow().write().allResources().inCompartment("Patient", pid1).andThen() - .build(); - } - }); - - Observation obs = new Observation(); - obs.setStatus(ObservationStatus.FINAL); - obs.setSubject(new Reference(pid1)); - IIdType oid = ourClient.create().resource(obs).execute().getId().toUnqualified(); - - - unregisterInterceptors(); - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow().write().allResources().inCompartment("Patient", pid2).andThen() - .build(); - } - }); - - /* - * Try to update to a new patient. The user has access to write to things in - * pid2's compartment, so this would normally be ok, but in this case they are overwriting - * a resource that is already in pid1's compartment, which shouldn't be allowed. - */ - obs = new Observation(); - obs.setId(oid); - obs.setStatus(ObservationStatus.FINAL); - obs.setSubject(new Reference(pid2)); - - try { - ourClient.update().resource(obs).execute(); - fail(); - } catch (ForbiddenOperationException e) { - // good - } - - } - - @Test - public void testCreateConditional() { - - Patient patient = new Patient(); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - final MethodOutcome output1 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute(); - - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/" + output1.getId().getIdPart())).andThen() - .allow().updateConditional().allResources() - .build(); - } - }); - - patient = new Patient(); - patient.setId(output1.getId().toUnqualifiedVersionless()); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - MethodOutcome output2 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute(); - - assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart()); - - patient = new Patient(); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - try { - ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|101").execute(); - fail(); - } catch (ForbiddenOperationException e) { - assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); - } - - patient = new Patient(); - patient.setId("999"); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - try { - ourClient.update().resource(patient).execute(); - fail(); - } catch (ForbiddenOperationException e) { - assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); - } - - } - - /** - * See #503 #751 - */ - @Test - public void testDeleteIsAllowedForCompartment() { - - Patient patient = new Patient(); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - final IIdType id = ourClient.create().resource(patient).execute().getId(); - - Observation obsInCompartment = new Observation(); - obsInCompartment.setStatus(ObservationStatus.FINAL); - obsInCompartment.getSubject().setReferenceElement(id.toUnqualifiedVersionless()); - IIdType obsInCompartmentId = ourClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless(); - - // create a 2nd observation to be deleted by url Observation?patient=id - ourClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless(); - - Observation obsNotInCompartment = new Observation(); - obsNotInCompartment.setStatus(ObservationStatus.FINAL); - IIdType obsNotInCompartmentId = ourClient.create().resource(obsNotInCompartment).execute().getId().toUnqualifiedVersionless(); - - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow().delete().resourcesOfType(Observation.class).inCompartment("Patient", id).andThen() - .deny().delete().allResources().withAnyId().andThen() - .allowAll() - .build(); - } - }); - - ourClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute(); - ourClient.delete().resourceConditionalByUrl("Observation?patient=" + id.toUnqualifiedVersionless()).execute(); - - try { - ourClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute(); - fail(); - } catch (ForbiddenOperationException e) { - // good - } - } - - /** - * See #503 - */ - @Test - public void testDeleteIsBlocked() { - - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .deny().delete().allResources().withAnyId().andThen() - .allowAll() - .build(); - } - }); - - Patient patient = new Patient(); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); - IIdType id = ourClient.create().resource(patient).execute().getId(); - - try { - ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute(); - fail(); - } catch (ForbiddenOperationException e) { - // good - } - - patient = ourClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute(); - assertEquals(id.getValue(), patient.getId()); - } - - @Test - public void testDeleteResourceConditional() throws IOException { - String methodName = "testDeleteResourceConditional"; - - Patient pt = new Patient(); - pt.addName().setFamily(methodName); - String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt); - - HttpPost post = new HttpPost(ourServerBase + "/Patient"); - post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - CloseableHttpResponse response = ourHttpClient.execute(post); - final IdType id; - try { - assertEquals(201, response.getStatusLine().getStatusCode()); - String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); - assertThat(newIdString, startsWith(ourServerBase + "/Patient/")); - id = new IdType(newIdString); - } finally { - response.close(); - } - - pt = new Patient(); - pt.addName().setFamily("FOOFOOFOO"); - resource = myFhirCtx.newXmlParser().encodeResourceToString(pt); - - post = new HttpPost(ourServerBase + "/Patient"); - post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - response = ourHttpClient.execute(post); - final IdType id2; - try { - assertEquals(201, response.getStatusLine().getStatusCode()); - String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); - assertThat(newIdString, startsWith(ourServerBase + "/Patient/")); - id2 = new IdType(newIdString); - } finally { - response.close(); - } - - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - //@formatter:off - return new RuleBuilder() - .allow("Rule 2").delete().allResources().inCompartment("Patient", new IdDt("Patient/" + id.getIdPart())).andThen() - .build(); - //@formatter:on - } - }); - - HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?name=" + methodName); - response = ourHttpClient.execute(delete); - try { - assertEquals(200, response.getStatusLine().getStatusCode()); - } finally { - response.close(); - } - - delete = new HttpDelete(ourServerBase + "/Patient?name=FOOFOOFOO"); - response = ourHttpClient.execute(delete); - try { - assertEquals(403, response.getStatusLine().getStatusCode()); - } finally { - response.close(); - } - - } - - private void unregisterInterceptors() { - for (IServerInterceptor next : new ArrayList(ourRestServer.getInterceptors())) { - if (next instanceof AuthorizationInterceptor) { - ourRestServer.unregisterInterceptor(next); - } - } - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java index 42bec3d1cd1..6a6728a9cea 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java @@ -71,8 +71,8 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { @After public void after() throws Exception { myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - myDaoConfig.getInterceptors().clear(); myResourceCountsCache.clear(); + ourRestServer.getInterceptorService().unregisterAllInterceptors(); } @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -90,7 +90,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { ourServerBase = "http://localhost:" + ourPort + "/fhir/context"; - ourRestServer.setResourceProviders((List) myResourceProviders); + ourRestServer.registerProviders(myResourceProviders.createProviders()); ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderInterceptorDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderInterceptorDstu3Test.java deleted file mode 100644 index b9119525c08..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderInterceptorDstu3Test.java +++ /dev/null @@ -1,258 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -import ca.uhn.fhir.jpa.provider.r4.ResourceProviderInterceptorR4Test; -import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.server.ResponseDetails; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; -import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.hl7.fhir.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.dstu3.model.Bundle.BundleType; -import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; -import org.hl7.fhir.dstu3.model.Organization; -import org.hl7.fhir.dstu3.model.Patient; -import org.hl7.fhir.dstu3.model.Reference; -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.Test; -import org.mockito.ArgumentCaptor; -import org.springframework.test.context.TestPropertySource; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -@TestPropertySource(properties = { - "scheduling_disabled=true" -}) -public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDstu3Test { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderInterceptorDstu3Test.class); - - private IServerInterceptor myDaoInterceptor; - private IServerInterceptor myServerInterceptor; - - @Override - @After - public void after() throws Exception { - super.after(); - - myDaoConfig.getInterceptors().remove(myDaoInterceptor); - ourRestServer.unregisterInterceptor(myServerInterceptor); - } - - @Override - public void before() throws Exception { - super.before(); - - myServerInterceptor = mock(IServerInterceptor.class, withSettings().verboseLogging()); - myDaoInterceptor = mock(IServerInterceptor.class, withSettings().verboseLogging()); - - resetServerInterceptor(); - - myDaoConfig.getInterceptors().add(myDaoInterceptor); - ourRestServer.registerInterceptor(myServerInterceptor); - - ourRestServer.registerInterceptor(new InterceptorAdapter() { - @Override - public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) { - super.incomingRequestPreHandled(theOperation, theProcessedRequest); - } - }); - - } - - private void resetServerInterceptor() throws ServletException, IOException { - reset(myServerInterceptor); - reset(myDaoInterceptor); - when(myServerInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true); - when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - } - - @Test - public void testCreateResource() throws IOException, ServletException { - String methodName = "testCreateResource"; - - Patient pt = new Patient(); - pt.addName().setFamily(methodName); - String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt); - - resetServerInterceptor(); - - HttpPost post = new HttpPost(ourServerBase + "/Patient"); - post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - CloseableHttpResponse response = ourHttpClient.execute(post); - try { - String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was: {}", resp); - assertEquals(201, response.getStatusLine().getStatusCode()); - String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); - assertThat(newIdString, startsWith(ourServerBase + "/Patient/")); - } finally { - response.close(); - } - - ArgumentCaptor ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - ArgumentCaptor opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myServerInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getValue()); - assertEquals("Patient", ardCaptor.getValue().getResourceType()); - assertNotNull(ardCaptor.getValue().getResource()); - - ResourceProviderInterceptorR4Test.verifyDaoInterceptor(myDaoInterceptor); - } - - @Test - public void testCreateResourceInTransaction() throws IOException, ServletException { - String methodName = "testCreateResourceInTransaction"; - - ourLog.info("** Starting {}", methodName); - - Patient pt = new Patient(); - pt.addName().setFamily(methodName); - - Bundle bundle = new Bundle(); - bundle.setType(BundleType.TRANSACTION); - BundleEntryComponent entry = bundle.addEntry(); - entry.setFullUrl("Patient"); - entry.setResource(pt); - entry.getRequest().setMethod(HTTPVerb.POST); - entry.getRequest().setUrl("Patient"); - - String resource = myFhirCtx.newXmlParser().encodeResourceToString(bundle); - - resetServerInterceptor(); - - ArgumentCaptor ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - ArgumentCaptor opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myDaoInterceptor, times(0)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - verify(myServerInterceptor, times(0)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - - HttpPost post = new HttpPost(ourServerBase + "/"); - post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - CloseableHttpResponse response = ourHttpClient.execute(post); - try { - assertEquals(200, response.getStatusLine().getStatusCode()); - } finally { - response.close(); - } - - /* - * Server Interceptor - */ - - ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myServerInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0)); - assertEquals(null, ardCaptor.getAllValues().get(0).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(0).getResource()); - assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1)); - assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(1).getResource()); - - ArgumentCaptor rdCaptor = ArgumentCaptor.forClass(RequestDetails.class); - ArgumentCaptor srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); - ArgumentCaptor sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class); - verify(myServerInterceptor, times(1)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture()); - - /* - * DAO Interceptor - */ - - ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myDaoInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0)); - assertEquals("Bundle", ardCaptor.getAllValues().get(0).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(0).getResource()); - assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1)); - assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType()); - assertNotNull(ardCaptor.getAllValues().get(1).getResource()); - - rdCaptor = ArgumentCaptor.forClass(RequestDetails.class); - srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); - sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class); - verify(myDaoInterceptor, times(0)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture()); - - } - - @Test - public void testCreateResourceWithVersionedReference() throws IOException, ServletException { - String methodName = "testCreateResourceWithVersionedReference"; - - Organization org = new Organization(); - org.setName("orgName"); - IIdType orgId = ourClient.create().resource(org).execute().getId().toUnqualified(); - assertNotNull(orgId.getVersionIdPartAsLong()); - - resetServerInterceptor(); - - Patient pt = new Patient(); - pt.addName().setFamily(methodName); - pt.setManagingOrganization(new Reference(orgId)); - - IParser parser = myFhirCtx.newXmlParser(); - parser.setDontStripVersionsFromReferencesAtPaths("Patient.managingOrganization"); - parser.setPrettyPrint(true); - String resource = parser.encodeResourceToString(pt); - - ourLog.info(resource); - - HttpPost post = new HttpPost(ourServerBase + "/Patient"); - post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - CloseableHttpResponse response = ourHttpClient.execute(post); - try { - String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was: {}", resp); - assertEquals(201, response.getStatusLine().getStatusCode()); - String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(); - assertThat(newIdString, startsWith(ourServerBase + "/Patient/")); - } finally { - response.close(); - } - - ArgumentCaptor ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - ArgumentCaptor opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myServerInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - - assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getValue()); - assertEquals("Patient", ardCaptor.getValue().getResourceType()); - assertNotNull(ardCaptor.getValue().getResource()); - - Patient patient; - patient = (Patient) ardCaptor.getAllValues().get(0).getResource(); - assertEquals(orgId.getValue(), patient.getManagingOrganization().getReference()); - - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java index a70f8a98982..f4dc3fc6156 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderQuestionnaireResponseDstu3Test.java @@ -49,9 +49,9 @@ public class ResourceProviderQuestionnaireResponseDstu3Test extends BaseResource for (IValidatorModule next : validators) { ourValidatingInterceptor.addValidatorModule(next); } - ourRestServer.registerInterceptor(ourValidatingInterceptor); } - + + ourRestServer.registerInterceptor(ourValidatingInterceptor); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java index 3c58314f65c..04b08891ff9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java @@ -13,6 +13,7 @@ import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType; import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus; 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; @@ -26,14 +27,18 @@ import static org.junit.Assert.*; public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionsDstu3Test.class); + private SubscriptionsRequireManualActivationInterceptorDstu3 myInterceptor; - @Override + @Before public void beforeCreateInterceptor() { - super.beforeCreateInterceptor(); + myInterceptor = new SubscriptionsRequireManualActivationInterceptorDstu3(); + myInterceptor.setDao(mySubscriptionDao); + myInterceptorRegistry.registerInterceptor(myInterceptor); + } - SubscriptionsRequireManualActivationInterceptorDstu3 interceptor = new SubscriptionsRequireManualActivationInterceptorDstu3(); - interceptor.setDao(mySubscriptionDao); - myDaoConfig.getInterceptors().add(interceptor); + @After + public void afterDestroyInterceptor() { + myInterceptorRegistry.unregisterInterceptor(myInterceptor); } @Before diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java index 347aab83d0e..7b8e741e57a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java @@ -8,7 +8,6 @@ import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; @@ -26,11 +25,9 @@ import org.junit.AfterClass; import org.junit.Test; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.*; @@ -40,7 +37,6 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource public void before() throws Exception { super.before(); myDaoConfig.setAllowMultipleDelete(true); - unregisterInterceptors(); } /** @@ -56,23 +52,23 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource pt2.setActive(false); final IIdType pid2 = ourClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless(); - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + AuthorizationInterceptor authInterceptor = new AuthorizationInterceptor(PolicyEnum.DENY) { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() .allow().write().allResources().inCompartment("Patient", pid1).andThen() .build(); } - }); + }; + ourRestServer.getInterceptorService().registerInterceptor(authInterceptor); Observation obs = new Observation(); obs.setStatus(ObservationStatus.FINAL); obs.setSubject(new Reference(pid1)); IIdType oid = ourClient.create().resource(obs).execute().getId().toUnqualified(); - - unregisterInterceptors(); - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + ourRestServer.getInterceptorService().unregisterInterceptor(authInterceptor); + ourRestServer.getInterceptorService().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() @@ -108,15 +104,13 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource patient.addName().setFamily("Tester").addGiven("Raghad"); final MethodOutcome output1 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute(); - ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + ourRestServer.getInterceptorService().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { @Override public List buildRuleList(RequestDetails theRequestDetails) { - //@formatter:off return new RuleBuilder() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/" + output1.getId().getIdPart())).andThen() + .allow("Rule 2").write().allResources().inCompartment("Patient", new IdType("Patient/" + output1.getId().getIdPart())).andThen() .allow().updateConditional().allResources() .build(); - //@formatter:on } }); @@ -673,14 +667,6 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource } } - private void unregisterInterceptors() { - for (IServerInterceptor next : new ArrayList<>(ourRestServer.getInterceptors())) { - if (next instanceof AuthorizationInterceptor) { - ourRestServer.unregisterInterceptor(next); - } - } - } - @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java index aa5695b9b38..cebd5b30458 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java @@ -81,6 +81,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { @After public void after() throws Exception { myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + ourRestServer.getInterceptorService().unregisterAllInterceptors(); } @SuppressWarnings({"unchecked", "rawtypes"}) @@ -97,7 +98,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { ourServerBase = "http://localhost:" + ourPort + "/fhir/context"; - ourRestServer.setResourceProviders((List) myResourceProviders); + ourRestServer.registerProviders(myResourceProviders.createProviders()); ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java index a2bbbc981c0..51b11790f92 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.jpa.dao.DaoConfig; -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 ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.util.TestUtil; @@ -58,7 +58,6 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { public void after() throws Exception { super.after(); - myInterceptorRegistry.clearAnonymousHookForUnitTest(); myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); } @@ -139,12 +138,12 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { public void testInterceptorHookIsCalledForAllContents_RESOURCE_MAY_BE_RETURNED() throws IOException { IAnonymousInterceptor pointcut = mock(IAnonymousInterceptor.class); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.RESOURCE_MAY_BE_RETURNED, pointcut); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PREACCESS_RESOURCE, pointcut); String theUrl = ourServerBase + "/" + compId + "/$document?_format=json"; fetchBundle(theUrl, EncodingEnum.JSON); - Mockito.verify(pointcut, times(10)).invoke(eq(Pointcut.RESOURCE_MAY_BE_RETURNED), myHookParamsCaptor.capture()); + Mockito.verify(pointcut, times(10)).invoke(eq(Pointcut.STORAGE_PREACCESS_RESOURCE), myHookParamsCaptor.capture()); List returnedClasses = myHookParamsCaptor .getAllValues() diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java index 67db89bd729..8387b3f4f07 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/HookInterceptorR4Test.java @@ -1,11 +1,12 @@ package ca.uhn.fhir.jpa.provider.r4; -import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Patient; +import org.junit.After; import org.junit.AfterClass; import org.junit.Test; @@ -15,9 +16,17 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(HookInterceptorR4Test.class); +// @Override +// @After +// public void after( ) throws Exception { +// super.after(); +// +// myInterceptorRegistry.unregisterAllInterceptors(); +// } + @Test public void testOP_PRESTORAGE_RESOURCE_CREATED_ModifyResource() { - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRESTORAGE_RESOURCE_CREATED, (thePointcut, t)->{ + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, (thePointcut, t)->{ Patient contents = (Patient) t.get(IBaseResource.class, 0); contents.getNameFirstRep().setFamily("NEWFAMILY"); }); @@ -36,7 +45,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test { @Test public void testOP_PRECOMMIT_RESOURCE_CREATED_ModifyResource() { - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRECOMMIT_RESOURCE_CREATED, (thePointcut, t)->{ + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, (thePointcut, t)->{ Patient contents = (Patient) t.get(IBaseResource.class, 0); contents.getNameFirstRep().setFamily("NEWFAMILY"); }); @@ -59,7 +68,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test { p.setActive(true); IIdType id = ourClient.create().resource(p).execute().getId(); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRESTORAGE_RESOURCE_UPDATED, (thePointcut, t)->{ + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, (thePointcut, t)->{ Patient contents = (Patient) t.get(IBaseResource.class, 1); contents.getNameFirstRep().setFamily("NEWFAMILY"); }); @@ -83,7 +92,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test { p.setActive(true); IIdType id = ourClient.create().resource(p).execute().getId(); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.OP_PRECOMMIT_RESOURCE_UPDATED, (thePointcut, t)->{ + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED, (thePointcut, t)->{ Patient contents = (Patient) t.get(IBaseResource.class, 1); contents.getNameFirstRep().setFamily("NEWFAMILY"); }); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java index 1b3000f799a..72234a1a537 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java @@ -1,9 +1,10 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.dao.DaoConfig; -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.jpa.interceptor.PerformanceTracingLoggingInterceptor; import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import ca.uhn.fhir.parser.IParser; @@ -25,7 +26,6 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.hamcrest.Matchers; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.*; @@ -46,9 +46,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; -import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; @@ -61,13 +59,16 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes private IServerOperationInterceptor myServerInterceptor; private List myInterceptors = new ArrayList<>(); + @Mock + private IAnonymousInterceptor myHook; + @Captor + private ArgumentCaptor myParamsCaptor; @Override @After public void after() throws Exception { super.after(); - myDaoConfig.getInterceptors().remove(myDaoInterceptor); myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); ourRestServer.unregisterInterceptor(myServerInterceptor); @@ -84,7 +85,6 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes resetServerInterceptor(); - myDaoConfig.getInterceptors().add(myDaoInterceptor); ourRestServer.registerInterceptor(myServerInterceptor); ourRestServer.registerInterceptor(new InterceptorAdapter() { @@ -102,43 +102,42 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes when(myServerInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); when(myServerInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myServerInterceptor.outgoingResponse(any(RequestDetails.class))).thenReturn(true); when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true); + when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); } - @Mock - private IAnonymousInterceptor myHook; - - @Captor - private ArgumentCaptor myParamsCaptor; - @Test - public void testPerfInterceptors() { + public void testPerfInterceptors() throws InterruptedException { myDaoConfig.setSearchPreFetchThresholds(Lists.newArrayList(15, 100)); for (int i = 0; i < 30; i++) { Patient p = new Patient(); - p.addName().setFamily("FAM"+i); + p.addName().setFamily("FAM" + i); ourLog.info("About to create patient"); myPatientDao.create(p); } IAnonymousInterceptor interceptor = mock(IAnonymousInterceptor.class); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED, interceptor); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_COMPLETE, interceptor); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_FAILED, interceptor); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE, interceptor); - myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE, interceptor); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED, interceptor); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE, interceptor); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.JPA_PERFTRACE_SEARCH_FAILED, interceptor); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE, interceptor); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE, interceptor); myInterceptors.add(interceptor); + myInterceptors.add(new PerformanceTracingLoggingInterceptor()); + ourLog.info("About to perform search..."); Bundle results = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).execute(); - verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture()); - verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture()); - verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture()); - verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture()); - verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture()); + + verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture()); + verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture()); + verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture()); + verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture()); + verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture()); SearchRuntimeDetails details = myParamsCaptor.getAllValues().get(0).get(SearchRuntimeDetails.class); assertEquals(SearchStatusEnum.PASSCMPLET, details.getSearchStatus()); @@ -146,11 +145,11 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes // Load the next (and final) page reset(interceptor); results = ourClient.loadPage().next(results).execute(); - verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture()); - verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture()); - verify(interceptor, times(1)).invoke(eq(Pointcut.PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture()); - verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture()); - verify(interceptor, times(0)).invoke(eq(Pointcut.PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture()); + verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture()); + verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture()); + verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture()); + verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture()); + verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture()); } @@ -195,11 +194,6 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes * DAO Interceptor */ - ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myDaoInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0)); - verify(myDaoInterceptor, times(0)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class)); verify(myDaoInterceptor, times(0)).resourceCreated(any(RequestDetails.class), any(IBaseResource.class)); verify(myDaoInterceptor, times(0)).resourceUpdated(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); @@ -234,7 +228,6 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes assertEquals("Patient", ardCaptor.getValue().getResourceType()); assertNotNull(ardCaptor.getValue().getResource()); - ResourceProviderInterceptorR4Test.verifyDaoInterceptor(myDaoInterceptor); } @Test @@ -273,27 +266,6 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes ArgumentCaptor sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class); verify(myServerInterceptor, times(1)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture()); - /* - * DAO Interceptor - */ - - /* - * Sometimes we get more than 2 hits of the incomingRequestPreHandled - * method. My working theory is that it's a scheduled background job, - * such as the subscription interceptor or the search parameter cache - * updating. - */ - ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myDaoInterceptor, atLeast(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertThat(ardCaptor.getAllValues().stream().map(ActionRequestDetails::getResourceType).collect(Collectors.toList()), Matchers.contains("Bundle", "Patient")); - assertThat(opTypeCaptor.getAllValues(), Matchers.contains(RestOperationTypeEnum.TRANSACTION, RestOperationTypeEnum.CREATE)); - - rdCaptor = ArgumentCaptor.forClass(RequestDetails.class); - srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); - sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class); - verify(myDaoInterceptor, times(0)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture()); - } @Test @@ -413,12 +385,6 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes * DAO Interceptor */ - ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class); - opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - verify(myDaoInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture()); - assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0)); - assertEquals(RestOperationTypeEnum.UPDATE, opTypeCaptor.getAllValues().get(1)); - verify(myDaoInterceptor, times(0)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class)); verify(myDaoInterceptor, times(0)).resourceCreated(any(RequestDetails.class), any(IBaseResource.class)); verify(myDaoInterceptor, times(0)).resourceUpdated(any(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class)); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java index 182785f483f..142faf093ce 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java @@ -1,85 +1,22 @@ package ca.uhn.fhir.jpa.provider.r4; -import ca.uhn.fhir.jpa.config.TestR4Config; -import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoRegistry; -import ca.uhn.fhir.jpa.entity.Search; -import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; -import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; -import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.jpa.util.TestUtil; -import ca.uhn.fhir.model.api.TemporalPrecisionEnum; -import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.model.primitive.UriDt; -import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.parser.StrictErrorHandler; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.PreferReturnEnum; -import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.api.IClientInterceptor; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.api.IHttpRequest; -import ca.uhn.fhir.rest.client.api.IHttpResponse; -import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; -import ca.uhn.fhir.rest.gclient.StringClientParam; -import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; -import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; -import ca.uhn.fhir.util.StopWatch; -import ca.uhn.fhir.util.UrlUtil; -import com.google.common.base.Charsets; -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.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.message.BasicNameValuePair; -import org.hamcrest.Matchers; -import org.hl7.fhir.instance.model.api.*; -import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator; -import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.r4.model.Bundle.*; -import org.hl7.fhir.r4.model.Encounter.EncounterLocationComponent; -import org.hl7.fhir.r4.model.Encounter.EncounterStatus; -import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; -import org.hl7.fhir.r4.model.Narrative.NarrativeStatus; -import org.hl7.fhir.r4.model.Observation.ObservationStatus; -import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; -import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType; -import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; -import org.junit.*; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Practitioner; +import org.junit.AfterClass; +import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.util.AopTestUtils; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.PostConstruct; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.math.BigDecimal; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.Collectors; -import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; @SuppressWarnings("Duplicates") @ContextConfiguration(classes = {ResourceProviderOnlySomeResourcesProvidedR4Test.OnlySomeResourcesProvidedCtxConfig.class}) @@ -95,8 +32,8 @@ public class ResourceProviderOnlySomeResourcesProvidedR4Test extends BaseResourc pract.setActive(true); try { ourClient.create().resource(pract).execute(); - } catch (InvalidRequestException e) { - assertEquals("", e.getMessage()); + } catch (ResourceNotFoundException e) { + assertThat(e.getMessage(), containsString("Unknown resource type 'Practitioner' - Server knows how to handle:")); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderQuestionnaireResponseR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderQuestionnaireResponseR4Test.java index ee1e4b81477..ab5e06768b0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderQuestionnaireResponseR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderQuestionnaireResponseR4Test.java @@ -49,9 +49,9 @@ public class ResourceProviderQuestionnaireResponseR4Test extends BaseResourcePro for (IValidatorModule next : validators) { ourValidatingInterceptor.addValidatorModule(next); } - ourRestServer.registerInterceptor(ourValidatingInterceptor); } - + + ourRestServer.getInterceptorService().registerInterceptor(ourValidatingInterceptor); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 1cdce063232..79d8bcfc0c5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -39,7 +39,8 @@ import java.util.stream.Collectors; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.TestUtil; -import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; @@ -98,9 +99,6 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; @@ -214,7 +212,17 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .returnBundle(Bundle.class) .execute(); - ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output)); + assertNull(output.getTotalElement().getValue()); + + output = ourClient + .search() + .forResource("Organization") + .count(3) + .totalMode(SearchTotalModeEnum.ACCURATE) + .returnBundle(Bundle.class) + .execute(); + + assertNotNull(output.getTotalElement().getValue()); String linkNext = output.getLink("next").getUrl(); linkNext = linkNext.replaceAll("_getpagesoffset=[0-9]+", "_getpagesoffset=3300"); @@ -1982,6 +1990,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { @Test public void testEverythingWithLargeSet2() { myDaoConfig.setSearchPreFetchThresholds(Arrays.asList(15, 30, -1)); + myPagingProvider.setDefaultPageSize(500); + myPagingProvider.setMaximumPageSize(1000); Patient p = new Patient(); p.setActive(true); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java index 77c114a9e77..abddabd05fb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java @@ -33,7 +33,7 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { SubscriptionsRequireManualActivationInterceptorR4 interceptor = new SubscriptionsRequireManualActivationInterceptorR4(); interceptor.setDao(mySubscriptionDao); - myDaoConfig.getInterceptors().add(interceptor); + myInterceptorRegistry.registerInterceptor(interceptor); } @Before diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java index 14345d5dd9f..864c4a5cbcc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java @@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.entity.SearchResult; -import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import ca.uhn.fhir.jpa.entity.SearchTypeEnum; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; @@ -40,6 +40,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.transaction.PlatformTransactionManager; import javax.persistence.EntityManager; +import java.io.IOException; import java.util.*; import static org.junit.Assert.*; @@ -454,6 +455,10 @@ public class SearchCoordinatorSvcImplTest { return myWrap.getSkippedCount(); } + @Override + public void close() throws IOException { + // nothing + } } public static class ResultIterator extends BaseIterator implements IResultIterator { @@ -478,6 +483,11 @@ public class SearchCoordinatorSvcImplTest { public int getSkippedCount() { return 0; } + + @Override + public void close() throws IOException { + // nothing + } } /** @@ -540,6 +550,10 @@ public class SearchCoordinatorSvcImplTest { } } + @Override + public void close() { + // nothing + } } @AfterClass diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java index 582ad7aaa57..b00b965a8f5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; +import static ca.uhn.fhir.jpa.subscription.resthook.RestHookTestDstu3Test.logAllInterceptors; import static org.junit.Assert.assertEquals; public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test { @@ -51,13 +52,20 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test { mySubscriptionTestUtil.unregisterSubscriptionInterceptor(); } + @Override @Before public void before() throws Exception { super.before(); + ourLog.info("Before re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); + mySubscriptionTestUtil.initEmailSender(ourListenerPort); mySubscriptionTestUtil.registerEmailInterceptor(); + + ourLog.info("After re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); } private Subscription createSubscription(String criteria, String payload, String endpoint) throws InterruptedException { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java index 24b7ff54f96..dffb9e12f67 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; +import static ca.uhn.fhir.jpa.subscription.resthook.RestHookTestDstu3Test.logAllInterceptors; import static org.junit.Assert.assertEquals; /** @@ -60,8 +61,15 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test { @Before public void beforeRegisterEmailListener() throws FolderException { ourTestSmtp.purgeEmailFromAllMailboxes(); + + ourLog.info("Before re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); + mySubscriptionTestUtil.registerEmailInterceptor(); + ourLog.info("After re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); + mySubscriptionTestUtil.initEmailSender(ourListenerPort); myDaoConfig.setEmailFromAddress("123@hapifhir.io"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java index 9c4177d7cc4..dba9e1588c1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java @@ -32,7 +32,9 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import static ca.uhn.fhir.jpa.subscription.resthook.RestHookTestDstu3Test.logAllInterceptors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -73,7 +75,11 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test { @Before public void beforeRegisterRestHookListener() { + ourLog.info("Before re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); mySubscriptionTestUtil.registerRestHookInterceptor(); + ourLog.info("After re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); } @Before @@ -146,6 +152,14 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test { Observation observation1 = sendObservation(code, "SNOMED-CT"); + String allInterceptors = myInterceptorRegistry + .getAllRegisteredInterceptors() + .stream() + .map(t->t.getClass().toString()) + .sorted() + .collect(Collectors.joining("\n * ")); + ourLog.info("Current interceptors:\n * {}", allInterceptors); + // Should see 1 subscription notification waitForQueueToDrain(); waitForSize(0, ourCreatedObservations); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java index 4d43488172a..71e38b0d7e4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java @@ -1,7 +1,7 @@ - package ca.uhn.fhir.jpa.subscription.resthook; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test; import ca.uhn.fhir.jpa.subscription.NotificationServlet; @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static org.junit.Assert.*; @@ -50,14 +51,13 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test { private static String ourListenerServerBase; private static List ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList()); private static List ourContentTypes = Collections.synchronizedList(new ArrayList<>()); - private List mySubscriptionIds = Collections.synchronizedList(new ArrayList<>()); - - @Autowired - private SubscriptionTestUtil mySubscriptionTestUtil; private static NotificationServlet ourNotificationServlet; private static String ourNotificationListenerServer; private static CountDownLatch communicationRequestListenerLatch; private static SubscriptionDebugLogInterceptor ourSubscriptionDebugLogInterceptor = new SubscriptionDebugLogInterceptor(); + private List mySubscriptionIds = Collections.synchronizedList(new ArrayList<>()); + @Autowired + private SubscriptionTestUtil mySubscriptionTestUtil; @After public void afterUnregisterRestHookListener() { @@ -81,8 +81,12 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test { @Before public void beforeRegisterRestHookListener() { + ourLog.info("Before re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); mySubscriptionTestUtil.registerRestHookInterceptor(); myInterceptorRegistry.registerInterceptor(ourSubscriptionDebugLogInterceptor); + ourLog.info("After re-registering interceptors"); + logAllInterceptors(myInterceptorRegistry); } @Before @@ -509,39 +513,6 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test { assertTrue("Timed out waiting for subscription to match", communicationRequestListenerLatch.await(10, TimeUnit.SECONDS)); } - @BeforeClass - public static void startListenerServer() throws Exception { - ourListenerPort = PortUtil.findFreePort(); - ourListenerRestServer = new RestfulServer(FhirContext.forDstu3()); - ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context"; - ourNotificationListenerServer = "http://localhost:" + ourListenerPort + "/fhir/subscription"; - - ObservationListener obsListener = new ObservationListener(); - CommunicationRequestListener crListener = new CommunicationRequestListener(); - ourListenerRestServer.setResourceProviders(obsListener, crListener); - - ourListenerServer = new Server(ourListenerPort); - ourNotificationServlet = new NotificationServlet(); - - ServletContextHandler proxyHandler = new ServletContextHandler(); - proxyHandler.setContextPath("/"); - - ServletHolder servletHolder = new ServletHolder(); - servletHolder.setServlet(ourListenerRestServer); - proxyHandler.addServlet(servletHolder, "/fhir/context/*"); - servletHolder = new ServletHolder(); - servletHolder.setServlet(ourNotificationServlet); - proxyHandler.addServlet(servletHolder, "/fhir/subscription"); - - ourListenerServer.setHandler(proxyHandler); - ourListenerServer.start(); - } - - @AfterClass - public static void stopListenerServer() throws Exception { - ourListenerServer.stop(); - } - public static class ObservationListener implements IResourceProvider { @Create @@ -587,4 +558,47 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test { return new MethodOutcome(new IdType("CommunicationRequest/1"), false); } } + + public static void logAllInterceptors(IInterceptorService theInterceptorRegistry) { + List allInterceptors = theInterceptorRegistry.getAllRegisteredInterceptors(); + String interceptorList = allInterceptors + .stream() + .map(t -> t.getClass().toString()) + .sorted() + .collect(Collectors.joining("\n * ")); + ourLog.info("Registered interceptors:\n * {}", interceptorList); + } + + @BeforeClass + public static void startListenerServer() throws Exception { + ourListenerPort = PortUtil.findFreePort(); + ourListenerRestServer = new RestfulServer(FhirContext.forDstu3()); + ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context"; + ourNotificationListenerServer = "http://localhost:" + ourListenerPort + "/fhir/subscription"; + + ObservationListener obsListener = new ObservationListener(); + CommunicationRequestListener crListener = new CommunicationRequestListener(); + ourListenerRestServer.setResourceProviders(obsListener, crListener); + + ourListenerServer = new Server(ourListenerPort); + ourNotificationServlet = new NotificationServlet(); + + ServletContextHandler proxyHandler = new ServletContextHandler(); + proxyHandler.setContextPath("/"); + + ServletHolder servletHolder = new ServletHolder(); + servletHolder.setServlet(ourListenerRestServer); + proxyHandler.addServlet(servletHolder, "/fhir/context/*"); + servletHolder = new ServletHolder(); + servletHolder.setServlet(ourNotificationServlet); + proxyHandler.addServlet(servletHolder, "/fhir/subscription"); + + ourListenerServer.setHandler(proxyHandler); + ourListenerServer.start(); + } + + @AfterClass + public static void stopListenerServer() throws Exception { + ourListenerServer.stop(); + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java index d4cc3aed05c..3f685522f6a 100755 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithInterceptorR4Test.java @@ -2,10 +2,10 @@ package ca.uhn.fhir.jpa.subscription.resthook; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.config.StoppableSubscriptionDeliveringRestHookSubscriber; -import ca.uhn.fhir.jpa.model.interceptor.api.Hook; -import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry; -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.IInterceptorService; +import ca.uhn.fhir.interceptor.api.Interceptor; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test; import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage; @@ -82,6 +82,8 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test { ourNextAfterRestHookDeliveryReturn = true; ourHitBeforeRestHookDelivery = false; ourHitAfterRestHookDelivery = false; + + myInterceptorRegistry.registerInterceptor(myTestInterceptor); } @Test @@ -300,18 +302,19 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test { } } + @Autowired + private IInterceptorService myInterceptorRegistry; + + @Autowired + private MyTestInterceptor myTestInterceptor; @Configuration static class MyTestCtxConfig { - @Autowired - private IInterceptorRegistry myInterceptorRegistry; @Bean public MyTestInterceptor interceptor() { - MyTestInterceptor retVal = new MyTestInterceptor(); - myInterceptorRegistry.registerInterceptor(retVal); - return retVal; + return new MyTestInterceptor(); } } @@ -343,9 +346,8 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test { } @Hook(Pointcut.SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY) - public boolean afterRestHookDelivery(ResourceDeliveryMessage theDeliveryMessage, CanonicalSubscription theSubscription) { + public void afterRestHookDelivery(ResourceDeliveryMessage theDeliveryMessage, CanonicalSubscription theSubscription) { ourHitAfterRestHookDelivery = true; - return ourNextAfterRestHookDeliveryReturn; } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/LoggingRule.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/LoggingRule.java index 9848b321ccb..348c632636e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/LoggingRule.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/LoggingRule.java @@ -49,7 +49,7 @@ public class LoggingRule implements TestRule { try { statement.evaluate(); } catch (final Throwable e) { - logger.info(MessageFormat.format("Exception thrown in test case [{0}]: {1}", description.getDisplayName(), e.toString())); + logger.error(MessageFormat.format("Exception thrown in test case [{0}]: {1}", description.getDisplayName(), e.toString()), e); throw e; } finally { logger.info(MessageFormat.format("Finished test case [{0}]", description.getDisplayName())); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/z/ZContextCloserDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/z/ZContextCloserDstu3Test.java deleted file mode 100644 index ea67128a701..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/z/ZContextCloserDstu3Test.java +++ /dev/null @@ -1,19 +0,0 @@ -package ca.uhn.fhir.jpa.z; - -import org.junit.Test; - -import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; - -public class ZContextCloserDstu3Test extends BaseJpaDstu3Test { - - /** - * this is just here to close the context when this package's test are done - */ - @Test -// @DirtiesContext() - public void testCloseContext() { - // nothing - } - - -} diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java index e3dc975982a..558f496ad79 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java @@ -24,7 +24,6 @@ import ca.uhn.fhir.util.StopWatch; import com.google.common.collect.ForwardingMap; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.concurrent.BasicThreadFactory; -import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.ColumnMapRowMapper; @@ -237,7 +236,7 @@ public class CalculateHashesTask extends BaseTableColumnTask - * 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("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: - *
    - *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • - *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • - *
  • ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchResult
  • - *
- *

- * 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: - *
    - *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • - *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • - *
- *

- * 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: - *

- *
    - *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • - *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • - *
- *

- * Hooks should return void. - *

- */ - 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: - *
    - *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • - *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • - *
- *

- * Hooks should return void. - *

- */ - 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: - *
    - *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • - *
  • ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage
  • - *
- *

- * 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: - *
    - *
  • ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription
  • - *
- *

- * Hooks should return void. - *

- */ - 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 (previous contents)
  • - *
  • 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 (previous contents)
  • - *
  • 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 theResource); - - void resourcesDeleted(Collection theResource); - - /** - * @deprecated Deprecated in HAPI FHIR 2.6 - Use {@link IRequestOperationCallback#resourceUpdated(IBaseResource, IBaseResource)} instead - */ - @Deprecated - void resourcesUpdated(Collection 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 theResource) { - myDeferredTasks.add(()-> myWrap.resourcesCreated(theResource)); - } - - @Override - public void resourcesDeleted(Collection theResource) { - myDeferredTasks.add(()-> myWrap.resourcesDeleted(theResource)); - } - - @Override - public void resourcesUpdated(Collection 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 theResource) { - for (IBaseResource next : theResource) { - resourceCreated(next); - } - } - - @Override - public void resourcesDeleted(Collection 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 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 false. In this case, no further processing will occur and no further interceptors * will be called. */ + @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED) boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse); /** @@ -150,6 +158,7 @@ public interface IServerInterceptor { * @deprecated As of HAPI FHIR 3.2.0, this method is deprecated and will be removed in a future version of HAPI FHIR. */ @Deprecated + @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) boolean outgoingResponse(RequestDetails theRequestDetails); /** @@ -158,6 +167,7 @@ public interface IServerInterceptor { * @deprecated As of HAPI FHIR 3.2.0, this method is deprecated and will be removed in a future version of HAPI FHIR. */ @Deprecated + @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException; /** @@ -166,6 +176,7 @@ public interface IServerInterceptor { * @deprecated As of HAPI FHIR 3.2.0, this method is deprecated and will be removed in a future version of HAPI FHIR. */ @Deprecated + @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject); /** @@ -191,6 +202,7 @@ public interface IServerInterceptor { * favour of {@link #outgoingResponse(RequestDetails, ResponseDetails, HttpServletRequest, HttpServletResponse)} * and will be removed in a future version of HAPI FHIR. */ + @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException; @@ -214,6 +226,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_OUTGOING_RESPONSE) boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException; @@ -256,6 +269,7 @@ public interface IServerInterceptor { * should always return null. If this interceptor adds an OperationOutcome to the exception, it * should return an exception. */ + @Hook(Pointcut.SERVER_PRE_PROCESS_OUTGOING_EXCEPTION) BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException; /** @@ -272,6 +286,7 @@ public interface IServerInterceptor { * * @param theRequestDetails The request itself */ + @Hook(Pointcut.SERVER_PROCESSING_COMPLETED_NORMALLY) void processingCompletedNormally(ServletRequestDetails theRequestDetails); class ActionRequestDetails { @@ -410,10 +425,14 @@ public interface IServerInterceptor { if (server == null) { return; } - List interceptors = server.getInterceptors(); - for (IServerInterceptor next : interceptors) { - next.incomingRequestPreHandled(theOperationType, this); - } + + IInterceptorService interceptorService = server.getInterceptorService(); + + HookParams params= new HookParams(); + params.add(RestOperationTypeEnum.class, theOperationType); + params.add(this); + interceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, params); + } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerOperationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerOperationInterceptor.java index 60edd8937a7..27f4d7f00eb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerOperationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerOperationInterceptor.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server.interceptor; * 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,6 +20,8 @@ package ca.uhn.fhir.rest.server.interceptor; * #L% */ +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -29,7 +31,11 @@ import org.hl7.fhir.instance.model.api.IBaseResource; * operations that call these sub-operations. * * @see ServerOperationInterceptorAdapter + * @deprecated Ths interface is no longer neccessary as of HAPI FHIR 3.8.0 - You can create + * interceptor methods that are declared using the {@link Hook} annotation without needing + * to implement any interceptor */ +@Deprecated public interface IServerOperationInterceptor extends IServerInterceptor { /** @@ -40,6 +46,7 @@ public interface IServerOperationInterceptor extends IServerInterceptor { * the transaction will be rolled back. *

*/ + @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED) void resourceCreated(RequestDetails theRequest, IBaseResource theResource); /** @@ -50,6 +57,7 @@ public interface IServerOperationInterceptor extends IServerInterceptor { * the transaction will be rolled back. *

*/ + @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED) void resourceDeleted(RequestDetails theRequest, IBaseResource theResource); /** @@ -67,6 +75,7 @@ public interface IServerOperationInterceptor extends IServerInterceptor { * to create. Interceptors may modify this * resource, and modifications will affect what is saved in the database. */ + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED) void resourcePreCreate(RequestDetails theRequest, IBaseResource theResource); /** @@ -79,6 +88,7 @@ public interface IServerOperationInterceptor extends IServerInterceptor { * * @param theResource The resource which is about to be deleted */ + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED) void resourcePreDelete(RequestDetails theRequest, IBaseResource theResource); /** @@ -98,12 +108,14 @@ public interface IServerOperationInterceptor extends IServerInterceptor { * to update to the resource to. Interceptors may modify this * resource, and modifications will affect what is saved in the database. */ + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED) void resourcePreUpdate(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource); /** * @deprecated Deprecated in HAPI FHIR 3.0.0 in favour of {@link #resourceUpdated(RequestDetails, IBaseResource, IBaseResource)} */ @Deprecated + @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED) void resourceUpdated(RequestDetails theRequest, IBaseResource theResource); /** @@ -118,6 +130,7 @@ public interface IServerOperationInterceptor extends IServerInterceptor { * convenient or possible to provide a value for this field, but servers should try to populate it. * @param theNewResource The resource as it will be after the update */ + @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED) void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java index c1f70795b55..56d1640e1a8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java @@ -1,8 +1,32 @@ package ca.uhn.fhir.rest.server.interceptor; -import static org.apache.commons.lang3.StringUtils.isNotBlank; +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.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.RestfulServerUtils; +import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.util.UrlUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.text.StringSubstitutor; +import org.apache.commons.text.lookup.StringLookup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.Date; +import java.util.Map.Entry; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; /* * #%L @@ -13,9 +37,9 @@ import java.io.IOException; * 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,29 +48,6 @@ import java.io.IOException; * #L% */ -import java.io.UnsupportedEncodingException; -import java.util.Date; -import java.util.Map.Entry; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import ca.uhn.fhir.util.UrlUtil; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.apache.commons.lang3.text.StrLookup; -import org.apache.commons.lang3.text.StrSubstitutor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.*; -import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; - /** * Server interceptor which logs each request using a defined format *

@@ -119,8 +120,8 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; * * */ - -public class LoggingInterceptor extends InterceptorAdapter { +@Interceptor +public class LoggingInterceptor { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class); @@ -143,12 +144,12 @@ public class LoggingInterceptor extends InterceptorAdapter { return myErrorMessageFormat; } - @Override + @Hook(Pointcut.SERVER_HANDLE_EXCEPTION) public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException { if (myLogExceptions) { // Perform any string substitutions from the message format - StrLookup lookup = new MyLookup(theServletRequest, theException, theRequestDetails); - StrSubstitutor subs = new StrSubstitutor(lookup, "${", "}", '\\'); + StringLookup lookup = new MyLookup(theServletRequest, theException, theRequestDetails); + StringSubstitutor subs = new StringSubstitutor(lookup, "${", "}", '\\'); // Actuall log the line String line = subs.replace(myErrorMessageFormat); @@ -159,11 +160,11 @@ public class LoggingInterceptor extends InterceptorAdapter { } - @Override + @Hook(Pointcut.SERVER_PROCESSING_COMPLETED_NORMALLY) public void processingCompletedNormally(ServletRequestDetails theRequestDetails) { // Perform any string substitutions from the message format - StrLookup lookup = new MyLookup(theRequestDetails.getServletRequest(), theRequestDetails); - StrSubstitutor subs = new StrSubstitutor(lookup, "${", "}", '\\'); + StringLookup lookup = new MyLookup(theRequestDetails.getServletRequest(), theRequestDetails); + StringSubstitutor subs = new StringSubstitutor(lookup, "${", "}", '\\'); // Actually log the line String line = subs.replace(myMessageFormat); @@ -212,7 +213,7 @@ public class LoggingInterceptor extends InterceptorAdapter { myMessageFormat = theMessageFormat; } - private static final class MyLookup extends StrLookup { + private static final class MyLookup implements StringLookup { private final Throwable myException; private final HttpServletRequest myRequest; private final RequestDetails myRequestDetails; @@ -223,7 +224,7 @@ public class LoggingInterceptor extends InterceptorAdapter { myException = null; } - public MyLookup(HttpServletRequest theServletRequest, BaseServerResponseException theException, RequestDetails theRequestDetails) { + MyLookup(HttpServletRequest theServletRequest, BaseServerResponseException theException, RequestDetails theRequestDetails) { myException = theException; myRequestDetails = theRequestDetails; myRequest = theServletRequest; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java index 4f0c9bdd7ed..2c9d0968bfb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java @@ -21,11 +21,15 @@ package ca.uhn.fhir.rest.server.interceptor.auth; */ import ca.uhn.fhir.context.FhirContext; +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.model.api.TagList; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter; import ca.uhn.fhir.util.CoverageIgnore; import com.google.common.collect.Lists; @@ -57,7 +61,8 @@ import static org.apache.commons.lang3.StringUtils.defaultString; * * @see SearchNarrowingInterceptor */ -public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter implements IRuleApplier { +@Interceptor +public class AuthorizationInterceptor implements IRuleApplier { private static final Logger ourLog = LoggerFactory.getLogger(AuthorizationInterceptor.class); @@ -280,8 +285,8 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter applyRulesAndFailIfDeny(operation, theRequest, theResource, theResource.getIdElement(), null); } - @Override - public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) { + @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED) + public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, IServerInterceptor.ActionRequestDetails theProcessedRequest) { IBaseResource inputResource = null; IIdType inputResourceId = null; @@ -303,7 +308,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter applyRulesAndFailIfDeny(theOperation, requestDetails, inputResource, inputResourceId, null); } - @Override + @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) { switch (determineOperationDirection(theRequestDetails.getRestOperationType(), null)) { case IN: @@ -348,31 +353,18 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter return true; } - @CoverageIgnore - @Override - public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject) { - throw failForDstu1(); - } - - @CoverageIgnore - @Override - public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) - throws AuthenticationException { - throw failForDstu1(); - } - - @Override - public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) { + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED) + public void resourcePreCreate(RequestDetails theRequest, IBaseResource theResource) { handleUserOperation(theRequest, theResource, RestOperationTypeEnum.CREATE); } - @Override - public void resourceDeleted(RequestDetails theRequest, IBaseResource theResource) { + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED) + public void resourcePreDelete(RequestDetails theRequest, IBaseResource theResource) { handleUserOperation(theRequest, theResource, RestOperationTypeEnum.DELETE); } - @Override - public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) { + @Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED) + public void resourcePreUpdate(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) { if (theOldResource != null) { handleUserOperation(theRequest, theOldResource, RestOperationTypeEnum.UPDATE); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java index 2df94c2cb41..854a753e5c2 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.rest.server.method; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; @@ -247,10 +249,15 @@ public abstract class BaseMethodBinding { // Handle server action interceptors RestOperationTypeEnum operationType = getRestOperationType(theRequest); if (operationType != null) { - for (IServerInterceptor next : theServer.getInterceptors()) { - ActionRequestDetails details = new ActionRequestDetails(theRequest); - populateActionRequestDetailsForInterceptor(theRequest, details, theMethodParams); - next.incomingRequestPreHandled(operationType, details); + ActionRequestDetails details = new ActionRequestDetails(theRequest); + populateActionRequestDetailsForInterceptor(theRequest, details, theMethodParams); + HookParams preHandledParams = new HookParams(); + preHandledParams.add(RestOperationTypeEnum.class, operationType); + preHandledParams.add(ActionRequestDetails.class, details); + if (theRequest.getInterceptorBroadcaster() != null) { + theRequest + .getInterceptorBroadcaster() + .callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, preHandledParams); } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java index 3ff86020154..f3ccaba9567 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java @@ -23,6 +23,8 @@ package ca.uhn.fhir.rest.server.method; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.server.IRestfulResponse; import ca.uhn.fhir.rest.api.server.IRestfulServer; @@ -215,24 +217,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding= 0; i--) { - IServerInterceptor next = theServer.getInterceptors().get(i); - boolean continueProcessing = next.outgoingResponse(theRequest, outcome); - if (!continueProcessing) { - return null; - } - - continueProcessing = next.outgoingResponse(theRequest, responseDetails, servletRequest, servletResponse); - if (!continueProcessing) { - return null; - } + if (!BaseResourceReturningMethodBinding.callOutgoingResponseHook(theRequest, responseDetails)) { + return null; } IRestfulResponse restfulResponse = theRequest.getResponse(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java index b6cc89a4534..3fcb1aba71a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java @@ -2,6 +2,8 @@ package ca.uhn.fhir.rest.server.method; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; @@ -18,7 +20,6 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.UrlUtil; @@ -378,24 +379,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi responseDetails.setResponseResource(response); responseDetails.setResponseCode(Constants.STATUS_HTTP_200_OK); - HttpServletRequest servletRequest = null; - HttpServletResponse servletResponse = null; - if (theRequest instanceof ServletRequestDetails) { - servletRequest = ((ServletRequestDetails) theRequest).getServletRequest(); - servletResponse = ((ServletRequestDetails) theRequest).getServletResponse(); - } - - for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) { - IServerInterceptor next = theServer.getInterceptors().get(i); - boolean continueProcessing = next.outgoingResponse(theRequest, response); - if (!continueProcessing) { - return null; - } - - continueProcessing = next.outgoingResponse(theRequest, responseDetails, servletRequest, servletResponse); - if (!continueProcessing) { - return null; - } + if (!callOutgoingResponseHook(theRequest, responseDetails)) { + return null; } boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest); @@ -427,4 +412,27 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi RESOURCE } + static boolean callOutgoingResponseHook(RequestDetails theRequest, ResponseDetails theResponseDetails) { + HttpServletRequest servletRequest = null; + HttpServletResponse servletResponse = null; + if (theRequest instanceof ServletRequestDetails) { + servletRequest = ((ServletRequestDetails) theRequest).getServletRequest(); + servletResponse = ((ServletRequestDetails) theRequest).getServletResponse(); + } + + HookParams responseParams = new HookParams(); + responseParams.add(RequestDetails.class, theRequest); + responseParams.addIfMatchesType(ServletRequestDetails.class, theRequest); + responseParams.add(IBaseResource.class, theResponseDetails.getResponseResource()); + responseParams.add(ResponseDetails.class, theResponseDetails); + responseParams.add(HttpServletRequest.class, servletRequest); + responseParams.add(HttpServletResponse.class, servletResponse); + if (theRequest.getInterceptorBroadcaster() != null) { + if (!theRequest.getInterceptorBroadcaster().callHooks(Pointcut.SERVER_OUTGOING_RESPONSE, responseParams)) { + return false; + } + } + return true; + } + } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/RequestOperationCallbackParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/InterceptorBroadcasterParameter.java similarity index 92% rename from hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/RequestOperationCallbackParameter.java rename to hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/InterceptorBroadcasterParameter.java index fbbc45ec520..6b5bfb60ea7 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/RequestOperationCallbackParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/InterceptorBroadcasterParameter.java @@ -27,11 +27,11 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -class RequestOperationCallbackParameter implements IParameter { +class InterceptorBroadcasterParameter implements IParameter { @Override public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { - return theRequest.getRequestOperationCallback(); + return theRequest.getInterceptorBroadcaster(); } @Override diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java index a5d25182292..be23e2d2adc 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java @@ -23,12 +23,12 @@ package ca.uhn.fhir.rest.server.method; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.IRequestOperationCallback; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.binder.CollectionBinder; import ca.uhn.fhir.rest.server.method.OperationParameter.IOperationParamConverter; @@ -122,8 +122,8 @@ public class MethodUtil { param = new ServletResponseParameter(); } else if (parameterType.equals(RequestDetails.class) || parameterType.equals(ServletRequestDetails.class)) { param = new RequestDetailsParameter(); - } else if (parameterType.equals(IRequestOperationCallback.class)) { - param = new RequestOperationCallbackParameter(); + } else if (parameterType.equals(IInterceptorBroadcaster.class)) { + param = new InterceptorBroadcasterParameter(); } else if (parameterType.equals(SummaryEnum.class)) { param = new SummaryEnumParameter(); } else if (parameterType.equals(PatchTypeEnum.class)) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java index d828f6abe7d..387e5353265 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.server.servlet; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.RestfulServer; @@ -48,8 +49,8 @@ public class ServletRequestDetails extends RequestDetails { private HttpServletRequest myServletRequest; private HttpServletResponse myServletResponse; - public ServletRequestDetails() { - super(); + public ServletRequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) { + super(theInterceptorBroadcaster); setResponse(new ServletRestfulResponse(this)); } @@ -101,7 +102,7 @@ public class ServletRequestDetails extends RequestDetails { @Override public List getHeaders(String name) { Enumeration headers = getServletRequest().getHeaders(name); - return headers == null ? Collections. emptyList() : Collections.list(getServletRequest().getHeaders(name)); + return headers == null ? Collections.emptyList() : Collections.list(getServletRequest().getHeaders(name)); } @Override diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java index f56e475f7a2..a032c6461b4 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.rest.server.servlet; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.rest.server.RestfulServer; import org.junit.Before; import org.junit.Rule; @@ -12,11 +13,11 @@ import org.mockito.junit.MockitoRule; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; - import java.io.IOException; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Unit tests of {@link ServletRestfulResponse}. @@ -42,7 +43,7 @@ public class ServletRestfulResponseTest { public void init() throws IOException { Mockito.when(servletResponse.getOutputStream()).thenReturn(servletOutputStream); - requestDetails = new ServletRequestDetails(); + requestDetails = new ServletRequestDetails(mock(IInterceptorBroadcaster.class)); requestDetails.setServer(server); requestDetails.setServletResponse(servletResponse); response = new ServletRestfulResponse(requestDetails); diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/client/RestfulClientFactoryTest.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/client/RestfulClientFactoryTest.java deleted file mode 100644 index 63607620375..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/client/RestfulClientFactoryTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package ca.uhn.fhir.rest.client; - -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.List; -import java.util.Map; - -import org.hl7.fhir.dstu2016may.model.Conformance; -import org.junit.AfterClass; -import org.junit.Test; -import org.mockito.Mockito; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.RequestTypeEnum; -import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; -import ca.uhn.fhir.rest.client.api.*; -import ca.uhn.fhir.rest.client.impl.BaseClient; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.util.TestUtil; - -@SuppressWarnings("deprecation") -public class RestfulClientFactoryTest { - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - - /** - * See #518 - */ - @SuppressWarnings({ "unchecked", "cast" }) - @Test - public void testMissingCapabilityStatement() throws Exception { - FhirContext ctx = mock(FhirContext.class); - IRestfulClientFactory restfulClientFactory = mock(IRestfulClientFactory.class); - IHttpClient httpClient = mock(IHttpClient.class); - - when(ctx.getResourceDefinition(Mockito.eq("CapabilityStatement"))).thenThrow(new DataFormatException()); - when(ctx.getResourceDefinition(Mockito.eq("Conformance"))).thenReturn(ourCtx.getResourceDefinition("Conformance")); - when(ctx.getResourceDefinition(Conformance.class)).thenReturn(ourCtx.getResourceDefinition("Conformance")); - when(ctx.getVersion()).thenReturn(FhirVersionEnum.DSTU2_1.getVersionImplementation()); - when(ctx.getRestfulClientFactory()).thenReturn(restfulClientFactory); - - when(restfulClientFactory.getHttpClient(any(StringBuilder.class), (Map>)nullable(Map.class), nullable(String.class), any(RequestTypeEnum.class), any(List.class))).thenReturn(httpClient); - - IHttpRequest httpRequest = mock(IHttpRequest.class); - when(httpClient.createGetRequest(any(FhirContext.class), nullable(EncodingEnum.class))).thenReturn(httpRequest); - - IHttpResponse httpResponse = mock(IHttpResponse.class); - when(httpRequest.execute()).thenReturn(httpResponse); - - when(httpResponse.getStatus()).thenReturn(404); - - ApacheRestfulClientFactory cf = new ApacheRestfulClientFactory(ctx); - IHttpClient client = mock(IHttpClient.class); - BaseClient baseClient = mock(BaseClient.class); - - try { - cf.validateServerBase("http://localhost:9999", client, baseClient); - fail(); - } catch (ResourceNotFoundException e) { - // ok - } - - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu2_1Test.java deleted file mode 100644 index 9b54ae480aa..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu2_1Test.java +++ /dev/null @@ -1,179 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.concurrent.TimeUnit; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import ca.uhn.fhir.rest.api.server.ResponseDetails; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.*; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Validate; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InterceptorDstu2_1Test { - private static final Logger ourLog = LoggerFactory.getLogger(InterceptorDstu2_1Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - private IServerInterceptor myInterceptor1; - private IServerInterceptor myInterceptor2; - - @Before - public void before() { - myInterceptor1 = mock(IServerInterceptor.class, withSettings().verboseLogging()); - myInterceptor2 = mock(IServerInterceptor.class, withSettings().verboseLogging()); - ourServlet.setInterceptors(myInterceptor1, myInterceptor2); - } - - - @Test - public void testValidate() throws Exception { - when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(any(ServletRequestDetails.class), any(OperationOutcome.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor2.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor2.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor2.outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); - when(myInterceptor2.outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - - //@formatter:off - String input = - "{\n" + - " \"resourceType\":\"Observation\",\n" + - " \"id\":\"1855669\",\n" + - " \"meta\":{\n" + - " \"versionId\":\"1\",\n" + - " \"lastUpdated\":\"2016-02-18T07:41:35.953-05:00\"\n" + - " },\n" + - " \"status\":\"final\",\n" + - " \"subject\":{\n" + - " \"reference\":\"Patient/1\"\n" + - " },\n" + - " \"effectiveDateTime\":\"2016-02-18T07:45:36-05:00\",\n" + - " \"valueQuantity\":{\n" + - " \"value\":57,\n" + - " \"system\":\"http://unitsofmeasure.org\",\n" + - " \"code\":\"{Beats}/min\"\n" + - " }\n" + - "}"; - //@formatter:on - - ourLog.info("Starting call"); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); - httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); - HttpResponse status = ourClient.execute(httpPost); - IOUtils.closeQuietly(status.getEntity().getContent()); - - InOrder order = inOrder(myInterceptor1, myInterceptor2); - order.verify(myInterceptor1, times(1)).incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - order.verify(myInterceptor2, times(1)).incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - order.verify(myInterceptor1, times(1)).incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - order.verify(myInterceptor2, times(1)).incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - ArgumentCaptor opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); - order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture()); - order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(nullable(RestOperationTypeEnum.class), nullable(ActionRequestDetails.class)); - order.verify(myInterceptor2, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class)); - order.verify(myInterceptor2, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - order.verify(myInterceptor1, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class)); - order.verify(myInterceptor1, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - - // Avoid concurrency issues - Thread.sleep(500); - - order.verify(myInterceptor2, times(1)).processingCompletedNormally(nullable(ServletRequestDetails.class)); - order.verify(myInterceptor1, times(1)).processingCompletedNormally(nullable(ServletRequestDetails.class)); - verifyNoMoreInteractions(myInterceptor1); - verifyNoMoreInteractions(myInterceptor2); - - assertEquals(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, opTypeCapt.getValue()); - assertNotNull(arTypeCapt.getValue().getResource()); - } - - @AfterClass - public static void afterClassClearContext() throws Exception { - ourServer.stop(); - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @After - public void after() { - for (IServerInterceptor next : new ArrayList(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } - } - - @BeforeClass - public static void beforeClass() throws Exception { - ourPort = PortUtil.findFreePort(); - ourServer = new Server(ourPort); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - ourServer.start(); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Validate() - public MethodOutcome validate(@ResourceParam Patient theResource) { - return new MethodOutcome(); - } - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java index 51ad6c04603..9b47d04379d 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java @@ -67,9 +67,7 @@ public class SearchPostDstu2_1Test { ourLastSortSpec = null; ourLastName = null; - for (IServerInterceptor next : new ArrayList(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); } /** diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index f6d88a86b8e..826faa6048c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -1,41 +1,12 @@ package ca.uhn.fhir.rest.client; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.either; -import static org.hamcrest.Matchers.emptyString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.*; -import java.nio.charset.Charset; -import java.util.*; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.input.ReaderInputStream; -import org.apache.http.*; -import org.apache.http.Header; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicStatusLine; -import org.hl7.fhir.instance.model.api.*; -import org.junit.*; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.interceptor.api.IInterceptorService; +import ca.uhn.fhir.model.api.ExtensionDt; +import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.*; @@ -44,7 +15,9 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu2.resource.Conformance.RestSecurity; import ca.uhn.fhir.model.primitive.*; -import ca.uhn.fhir.parser.*; +import ca.uhn.fhir.parser.DataFormatException; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.parser.XmlParserDstu2Test; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; import ca.uhn.fhir.rest.client.api.*; @@ -54,11 +27,45 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.util.TestUtil; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.ReaderInputStream; +import org.apache.http.Header; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicStatusLine; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.util.*; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class GenericClientDstu2Test { - private static FhirContext ourCtx; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu2Test.class); + private static FhirContext ourCtx; private HttpClient myHttpClient; private HttpResponse myHttpResponse; @@ -89,21 +96,21 @@ public class GenericClientDstu2Test { private String getPatientFeedWithOneResult() { //@formatter:off - String msg = "\n" + - "d039f91a-cc3c-4013-988e-af4d8d0614bd\n" + - "\n" + - "" - + "" - + "

John Cardinal: 444333333
" - + "" - + "" - + "" - + "" - + "
" - + "" - + "\n" - + " \n" - + ""; + String msg = "\n" + + "d039f91a-cc3c-4013-988e-af4d8d0614bd\n" + + "\n" + + "" + + "" + + "
John Cardinal: 444333333
" + + "" + + "" + + "" + + "" + + "
" + + "
" + + "
\n" + + "
\n" + + "
"; //@formatter:on return msg; } @@ -260,7 +267,7 @@ public class GenericClientDstu2Test { int idx = 0; //@formatter:off - Conformance resp = (Conformance)client.fetchConformance().ofType(Conformance.class).execute(); + Conformance resp = (Conformance) client.fetchConformance().ofType(Conformance.class).execute(); //@formatter:on assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(idx).getURI().toASCIIString()); @@ -636,10 +643,10 @@ public class GenericClientDstu2Test { //@formatter:off response = client - .history() - .onServer() - .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .history() + .onServer() + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/_history", capt.getAllValues().get(idx).getURI().toString()); assertEquals(1, response.getEntry().size()); @@ -647,12 +654,12 @@ public class GenericClientDstu2Test { //@formatter:off response = client - .history() - .onServer() - .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .since((Date)null) - .count(null) - .execute(); + .history() + .onServer() + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .since((Date) null) + .count(null) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/_history", capt.getAllValues().get(idx).getURI().toString()); assertEquals(1, response.getEntry().size()); @@ -660,11 +667,11 @@ public class GenericClientDstu2Test { //@formatter:off response = client - .history() - .onServer() - .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .since(new InstantDt()) - .execute(); + .history() + .onServer() + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .since(new InstantDt()) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/_history", capt.getAllValues().get(idx).getURI().toString()); assertEquals(1, response.getEntry().size()); @@ -672,10 +679,10 @@ public class GenericClientDstu2Test { //@formatter:off response = client - .history() - .onType(Patient.class) - .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .history() + .onType(Patient.class) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/_history", capt.getAllValues().get(idx).getURI().toString()); assertEquals(1, response.getEntry().size()); @@ -683,10 +690,10 @@ public class GenericClientDstu2Test { //@formatter:off response = client - .history() - .onInstance(new IdDt("Patient", "123")) - .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .history() + .onInstance(new IdDt("Patient", "123")) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/123/_history", capt.getAllValues().get(idx).getURI().toString()); assertEquals(1, response.getEntry().size()); @@ -694,25 +701,25 @@ public class GenericClientDstu2Test { //@formatter:off response = client - .history() - .onInstance(new IdDt("Patient", "123")) - .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .count(123) - .since(new InstantDt("2001-01-02T11:22:33Z")) - .execute(); + .history() + .onInstance(new IdDt("Patient", "123")) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .count(123) + .since(new InstantDt("2001-01-02T11:22:33Z")) + .execute(); //@formatter:on assertThat(capt.getAllValues().get(idx).getURI().toString(), either(equalTo("http://example.com/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")) - .or(equalTo("http://example.com/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + .or(equalTo("http://example.com/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); idx++; //@formatter:off response = client - .history() - .onInstance(new IdDt("Patient", "123")) - .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .since(new InstantDt("2001-01-02T11:22:33Z").getValue()) - .execute(); + .history() + .onInstance(new IdDt("Patient", "123")) + .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .since(new InstantDt("2001-01-02T11:22:33Z").getValue()) + .execute(); //@formatter:on assertThat(capt.getAllValues().get(idx).getURI().toString(), containsString("_since=2001-01")); assertEquals(1, response.getEntry().size()); @@ -756,17 +763,17 @@ public class GenericClientDstu2Test { //@formatter:off MetaDt resp = client - .meta() - .add() - .onResource(new IdDt("Patient/123")) - .meta(inMeta) - .execute(); + .meta() + .add() + .onResource(new IdDt("Patient/123")) + .meta(inMeta) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/123/$meta-add", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals("", - extractBody(capt, idx)); + extractBody(capt, idx)); idx++; } @@ -799,10 +806,10 @@ public class GenericClientDstu2Test { //@formatter:off MetaDt resp = client - .meta() - .get(MetaDt.class) - .fromServer() - .execute(); + .meta() + .get(MetaDt.class) + .fromServer() + .execute(); //@formatter:on assertEquals("http://example.com/fhir/$meta", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); @@ -811,10 +818,10 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .meta() - .get(MetaDt.class) - .fromType("Patient") - .execute(); + .meta() + .get(MetaDt.class) + .fromType("Patient") + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/$meta", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); @@ -823,10 +830,10 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .meta() - .get(MetaDt.class) - .fromResource(new IdDt("Patient/123")) - .execute(); + .meta() + .get(MetaDt.class) + .fromResource(new IdDt("Patient/123")) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/123/$meta", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); @@ -866,12 +873,12 @@ public class GenericClientDstu2Test { //@formatter:off Parameters resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withParameters(inParams) - .useHttpGet() - .execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -880,12 +887,12 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onType(Patient.class) - .named("$SOMEOPERATION") - .withParameters(inParams) - .useHttpGet() - .execute(); + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -894,12 +901,12 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onInstance(new IdDt("Patient", "123")) - .named("$SOMEOPERATION") - .withParameters(inParams) - .useHttpGet() - .execute(); + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -908,12 +915,12 @@ public class GenericClientDstu2Test { // @formatter:off resp = client - .operation() - .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) - .named("$SOMEOPERATION") - .withParameters(inParams) - .useHttpGet() - .execute(); + .operation() + .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withParameters(inParams) + .useHttpGet() + .execute(); // @formatter:on assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", capt.getAllValues().get(idx).getURI().toASCIIString()); idx++; @@ -945,12 +952,12 @@ public class GenericClientDstu2Test { //@formatter:off Parameters resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class) - .useHttpGet() - .execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -959,12 +966,12 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onType(Patient.class) - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class) - .useHttpGet() - .execute(); + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -973,12 +980,12 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onInstance(new IdDt("Patient", "123")) - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class) - .useHttpGet() - .execute(); + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -987,12 +994,12 @@ public class GenericClientDstu2Test { // @formatter:off resp = client - .operation() - .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class) - .useHttpGet() - .execute(); + .operation() + .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .useHttpGet() + .execute(); // @formatter:on assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); idx++; @@ -1067,10 +1074,10 @@ public class GenericClientDstu2Test { //@formatter:off Parameters resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withParameters(inParams).execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length); @@ -1108,12 +1115,12 @@ public class GenericClientDstu2Test { //@formatter:off Parameters resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withParameter(Parameters.class, "name1", new StringDt("value1")) - .andParameter("name2", new StringDt("value1")) - .execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new StringDt("value1")) + .andParameter("name2", new StringDt("value1")) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1121,8 +1128,8 @@ public class GenericClientDstu2Test { assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals( - "", - (extractBody(capt, idx))); + "", + (extractBody(capt, idx))); idx++; /* @@ -1131,12 +1138,12 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1")) - .andParameter("name2", new StringDt("value1")) - .execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1")) + .andParameter("name2", new StringDt("value1")) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1144,8 +1151,8 @@ public class GenericClientDstu2Test { assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals( - "", - (extractBody(capt, idx))); + "", + (extractBody(capt, idx))); idx++; /* @@ -1154,12 +1161,12 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1")) - .andParameter("name2", new Patient().setActive(true)) - .execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", new IdentifierDt("system1", "value1")) + .andParameter("name2", new Patient().setActive(true)) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1167,8 +1174,8 @@ public class GenericClientDstu2Test { assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals( - "", - (extractBody(capt, idx))); + "", + (extractBody(capt, idx))); idx++; } @@ -1204,11 +1211,11 @@ public class GenericClientDstu2Test { //@formatter:off client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withParameter(Parameters.class, "name1", weirdBase) - .execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameter(Parameters.class, "name1", weirdBase) + .execute(); //@formatter:on } @@ -1243,10 +1250,10 @@ public class GenericClientDstu2Test { //@formatter:off Parameters resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withParameters(inParams).execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1258,10 +1265,10 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onType(Patient.class) - .named("$SOMEOPERATION") - .withParameters(inParams).execute(); + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1273,10 +1280,10 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onInstance(new IdDt("Patient", "123")) - .named("$SOMEOPERATION") - .withParameters(inParams).execute(); + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withParameters(inParams).execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1321,10 +1328,10 @@ public class GenericClientDstu2Test { //@formatter:off Parameters resp = client - .operation() - .onServer() - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class).execute(); + .operation() + .onServer() + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); //@formatter:on assertEquals("http://example.com/fhir/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1336,10 +1343,10 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onType(Patient.class) - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class).execute(); + .operation() + .onType(Patient.class) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1351,10 +1358,10 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .operation() - .onInstance(new IdDt("Patient", "123")) - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class).execute(); + .operation() + .onInstance(new IdDt("Patient", "123")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class).execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals(respString, p.encodeResourceToString(resp)); @@ -1366,11 +1373,11 @@ public class GenericClientDstu2Test { // @formatter:off resp = client - .operation() - .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) - .named("$SOMEOPERATION") - .withNoParameters(Parameters.class) - .execute(); + .operation() + .onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")) + .named("$SOMEOPERATION") + .withNoParameters(Parameters.class) + .execute(); // @formatter:on assertEquals("http://example.com/fhir/Patient/123/$SOMEOPERATION", capt.getAllValues().get(idx).getURI().toASCIIString()); idx++; @@ -1402,31 +1409,31 @@ public class GenericClientDstu2Test { //@formatter:off client - .operation() - .onInstance(new IdDt("http://foo/Patient/1")) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeDt("8495-4")) - .andParameter("system", new UriDt("http://loinc.org")) - .useHttpGet() - .execute(); + .operation() + .onInstance(new IdDt("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeDt("8495-4")) + .andParameter("system", new UriDt("http://loinc.org")) + .useHttpGet() + .execute(); //@formatter:off - + assertEquals("http://example.com/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", capt.getAllValues().get(idx).getURI().toASCIIString()); //@formatter:off idx++; client - .operation() - .onInstance(new IdDt("http://foo/Patient/1")) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeDt("8495-4")) - .andParameter("system", new UriDt("http://loinc.org")) - .execute(); + .operation() + .onInstance(new IdDt("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeDt("8495-4")) + .andParameter("system", new UriDt("http://loinc.org")) + .execute(); //@formatter:off - + assertEquals("http://example.com/fhir/Patient/1/$validate-code", capt.getAllValues().get(idx).getURI().toASCIIString()); ourLog.info(extractBody(capt, idx)); - assertEquals("",extractBody(capt, idx)); + assertEquals("", extractBody(capt, idx)); } @@ -1453,9 +1460,9 @@ public class GenericClientDstu2Test { //@formatter:off ca.uhn.fhir.model.dstu2.resource.Bundle resp = client - .loadPage() - .next(sourceBundle) - .execute(); + .loadPage() + .next(sourceBundle) + .execute(); //@formatter:on assertEquals(1, resp.getEntry().size()); @@ -1498,9 +1505,9 @@ public class GenericClientDstu2Test { //@formatter:off ca.uhn.fhir.model.dstu2.resource.Bundle resp = client - .loadPage() - .previous(sourceBundle) - .execute(); + .loadPage() + .previous(sourceBundle) + .execute(); //@formatter:on assertEquals(1, resp.getEntry().size()); @@ -1516,9 +1523,9 @@ public class GenericClientDstu2Test { //@formatter:off resp = client - .loadPage() - .previous(sourceBundle) - .execute(); + .loadPage() + .previous(sourceBundle) + .execute(); //@formatter:on assertEquals(1, resp.getEntry().size()); @@ -1553,7 +1560,7 @@ public class GenericClientDstu2Test { when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); - when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") }); + when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT")}); when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock theInvocation) throws Throwable { @@ -1582,7 +1589,7 @@ public class GenericClientDstu2Test { when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); - when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") }); + when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT")}); when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock theInvocation) throws Throwable { @@ -1622,26 +1629,26 @@ public class GenericClientDstu2Test { public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception { //@formatter:off - final String input = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - ""; + final String input = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; //@formatter:on ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); - when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") }); + when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT")}); when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock theInvocation) throws Throwable { @@ -1655,10 +1662,10 @@ public class GenericClientDstu2Test { //@formatter:off response = client - .search() - .forResource(Patient.class) - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .search() + .forResource(Patient.class) + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("2015-06-22T15:48:57.554-04:00", ResourceMetadataKeyEnum.UPDATED.get(response).getValueAsString()); @@ -1677,15 +1684,15 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); //@formatter:off - IBaseResource response = client.read() - .resource("Patient") - .withId("123") - .elementsSubset("name", "identifier") - .execute(); - //@formatter:on + IBaseResource response = client.read() + .resource("Patient") + .withId("123") + .elementsSubset("name", "identifier") + .execute(); + //@formatter:on assertThat(capt.getValue().getURI().toString(), - either(equalTo("http://example.com/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient/123?_elements=identifier%2Cname"))); + either(equalTo("http://example.com/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1825,12 +1832,12 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .elementsSubset("name", "identifier") - .usingStyle(SearchStyleEnum.POST) - .returnBundle(Bundle.class) - .execute(); + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .returnBundle(Bundle.class) + .execute(); assertEquals("http://example.com/fhir/Patient/_search?_elements=identifier%2Cname", capt.getValue().getURI().toString()); @@ -1864,13 +1871,13 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .elementsSubset("name", "identifier") - .usingStyle(SearchStyleEnum.POST) - .encodedJson() - .returnBundle(Bundle.class) - .execute(); + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .encodedJson() + .returnBundle(Bundle.class) + .execute(); assertThat(capt.getValue().getURI().toString(), containsString("http://example.com/fhir/Patient/_search?")); assertThat(capt.getValue().getURI().toString(), containsString("_elements=identifier%2Cname")); @@ -1905,10 +1912,10 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .returnBundle(Bundle.class) - .execute(); + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .returnBundle(Bundle.class) + .execute(); assertEquals("http://example.com/fhir/Patient?name=james", capt.getValue().getURI().toString()); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); @@ -1936,10 +1943,10 @@ public class GenericClientDstu2Test { //@formatter:off ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() - .byUrl("http://foo?name=http://foo|bar") - .encodedJson() - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .byUrl("http://foo?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://foo?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); assertNotNull(response); @@ -1947,10 +1954,10 @@ public class GenericClientDstu2Test { //@formatter:off response = client.search() - .byUrl("Patient?name=http://foo|bar") - .encodedJson() - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .byUrl("Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); assertNotNull(response); @@ -1958,10 +1965,10 @@ public class GenericClientDstu2Test { //@formatter:off response = client.search() - .byUrl("/Patient?name=http://foo|bar") - .encodedJson() - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .byUrl("/Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); assertNotNull(response); @@ -1969,9 +1976,9 @@ public class GenericClientDstu2Test { //@formatter:off response = client.search() - .byUrl("Patient") - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .byUrl("Patient") + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString()); assertNotNull(response); @@ -1979,9 +1986,9 @@ public class GenericClientDstu2Test { //@formatter:off response = client.search() - .byUrl("Patient?") - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); + .byUrl("Patient?") + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); //@formatter:on assertEquals("http://example.com/fhir/Patient?", capt.getAllValues().get(idx).getURI().toString()); assertNotNull(response); @@ -2039,14 +2046,14 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .elementsSubset("name", "identifier") - .returnBundle(Bundle.class) - .execute(); + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .returnBundle(Bundle.class) + .execute(); assertThat(capt.getValue().getURI().toString(), - either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); + either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -2064,11 +2071,11 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .lastUpdated(new DateRangeParam("2011-01-01", "2012-01-01")) - .returnBundle(Bundle.class) - .execute(); + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .lastUpdated(new DateRangeParam("2011-01-01", "2012-01-01")) + .returnBundle(Bundle.class) + .execute(); assertEquals("http://example.com/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", capt.getValue().getURI().toString()); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); @@ -2090,11 +2097,11 @@ public class GenericClientDstu2Test { HashMap> params = new HashMap>(); params.put("foo", Arrays.asList((IQueryParameterType) new DateParam("2001"))); Bundle response = client - .search() - .forResource(Patient.class) - .where(params) - .returnBundle(Bundle.class) - .execute(); + .search() + .forResource(Patient.class) + .where(params) + .returnBundle(Bundle.class) + .execute(); assertEquals("http://example.com/fhir/Patient?foo=2001", capt.getValue().getURI().toString()); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); @@ -2114,13 +2121,13 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource("Patient") - .withProfile("http://foo1") - .withProfile("http://foo2") - .withSecurity("system1", "code1") - .withSecurity("system2", "code2") - .returnBundle(Bundle.class) - .execute(); + .forResource("Patient") + .withProfile("http://foo1") + .withProfile("http://foo2") + .withSecurity("system1", "code1") + .withSecurity("system2", "code2") + .returnBundle(Bundle.class) + .execute(); assertEquals("http://example.com/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", capt.getValue().getURI().toString()); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); @@ -2142,11 +2149,11 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource(Patient.class) - .encodedJson() - .revInclude(new Include("Provenance:target")) - .returnBundle(Bundle.class) - .execute(); + .forResource(Patient.class) + .encodedJson() + .revInclude(new Include("Provenance:target")) + .returnBundle(Bundle.class) + .execute(); assertEquals("http://example.com/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", capt.getValue().getURI().toString()); @@ -2165,11 +2172,11 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); Bundle response = client.search() - .forResource("Patient") - .where(Patient.NAME.matches().value("james")) - .summaryMode(SummaryEnum.FALSE) - .returnBundle(Bundle.class) - .execute(); + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .summaryMode(SummaryEnum.FALSE) + .returnBundle(Bundle.class) + .execute(); assertEquals("http://example.com/fhir/Patient?name=james&_summary=false", capt.getValue().getURI().toString()); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); @@ -2204,11 +2211,11 @@ public class GenericClientDstu2Test { input.add(p2); //@formatter:off - List response = client.transaction() - .withResources(input) - .encodedJson() - .execute(); - //@formatter:on + List response = client.transaction() + .withResources(input) + .encodedJson() + .execute(); + //@formatter:on assertEquals("http://example.com/fhir", capt.getValue().getURI().toString()); assertEquals(2, response.size()); @@ -2258,10 +2265,10 @@ public class GenericClientDstu2Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); //@formatter:off - String response = client.transaction() - .withBundle(reqStringJson) - .execute(); - //@formatter:on + String response = client.transaction() + .withBundle(reqStringJson) + .execute(); + //@formatter:on assertEquals("http://example.com/fhir/", capt.getValue().getURI().toString()); assertEquals(respStringJson, response); @@ -2271,11 +2278,11 @@ public class GenericClientDstu2Test { assertEquals("application/json+fhir; charset=UTF-8", capt.getValue().getFirstHeader("Content-Type").getValue()); //@formatter:off - response = client.transaction() - .withBundle(reqStringJson) - .encodedXml() - .execute(); - //@formatter:on + response = client.transaction() + .withBundle(reqStringJson) + .encodedXml() + .execute(); + //@formatter:on assertEquals("http://example.com/fhir/", capt.getValue().getURI().toString()); assertEquals(respStringJson, response); @@ -2314,11 +2321,11 @@ public class GenericClientDstu2Test { input.addEntry().setResource(p2); //@formatter:off - ca.uhn.fhir.model.dstu2.resource.Bundle response = client.transaction() - .withBundle(input) - .encodedJson() - .execute(); - //@formatter:on + ca.uhn.fhir.model.dstu2.resource.Bundle response = client.transaction() + .withBundle(input) + .encodedJson() + .execute(); + //@formatter:on assertEquals("http://example.com/fhir", capt.getValue().getURI().toString()); assertEquals(2, response.getEntry().size()); @@ -2515,8 +2522,8 @@ public class GenericClientDstu2Test { assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals("application/xml+fhir", capt.getAllValues().get(idx).getFirstHeader("content-type").getValue().replaceAll(";.*", "")); assertEquals( - "", - extractBody(capt, idx)); + "", + extractBody(capt, idx)); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); idx++; @@ -2526,8 +2533,8 @@ public class GenericClientDstu2Test { assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals("application/xml+fhir", capt.getAllValues().get(idx).getFirstHeader("content-type").getValue().replaceAll(";.*", "")); assertEquals( - "", - extractBody(capt, idx)); + "", + extractBody(capt, idx)); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); idx++; @@ -2584,8 +2591,8 @@ public class GenericClientDstu2Test { assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); assertEquals( - "", - extractBody(capt, idx)); + "", + extractBody(capt, idx)); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); idx++; @@ -2595,17 +2602,17 @@ public class GenericClientDstu2Test { return (OperationOutcome) theOperationOutcome; } - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @BeforeClass - public static void beforeClass() { - ourCtx = FhirContext.forDstu2(); - } - public final static class RestfulClientInstance implements IRestfulClient { + @Override + public IInterceptorService getInterceptorService() { + return null; + } + + @Override + public void setInterceptorService(@Nonnull IInterceptorService theInterceptorService) { + // nothing + } + @Override public T fetchResourceFromUrl(Class theResourceType, String theUrl) { return null; @@ -2631,11 +2638,6 @@ public class GenericClientDstu2Test { // nothing } - @Override - public void setEncoding(EncodingEnum theEncoding) { - // nothing - } - @Override public void setPrettyPrint(Boolean thePrettyPrint) { // nothing @@ -2663,10 +2665,20 @@ public class GenericClientDstu2Test { } @Override - public List getInterceptors() { + public void setEncoding(EncodingEnum theEncoding) { // nothing - return null; } + + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @BeforeClass + public static void beforeClass() { + ourCtx = FhirContext.forDstu2(); } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/RestfulClientFactoryDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/RestfulClientFactoryDstu2Test.java index 18e138fcab3..16b15fcfb42 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/RestfulClientFactoryDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/RestfulClientFactoryDstu2Test.java @@ -3,8 +3,7 @@ package ca.uhn.fhir.rest.client; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import java.util.List; import java.util.Map; @@ -56,8 +55,8 @@ public class RestfulClientFactoryDstu2Test { when(httpResponse.getStatus()).thenReturn(404); ApacheRestfulClientFactory cf = new ApacheRestfulClientFactory(ctx); - IHttpClient client = mock(IHttpClient.class); - BaseClient baseClient = mock(BaseClient.class); + IHttpClient client = mock(IHttpClient.class, withSettings().defaultAnswer(RETURNS_DEEP_STUBS)); + BaseClient baseClient = mock(BaseClient.class, withSettings().defaultAnswer(RETURNS_DEEP_STUBS)); try { cf.validateServerBase("http://localhost:9999", client, baseClient); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java index df8553aa666..bd7275c374b 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java @@ -11,8 +11,13 @@ import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.TimeUnit; +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.rest.server.servlet.ServletRequestDetails; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; @@ -49,7 +54,6 @@ public class InterceptorUserDataMapDstu2Test { private static int ourPort; private static Server ourServer; private static RestfulServer servlet; - private IServerInterceptor myInterceptor; private final Object myKey = "KEY"; private Map myMap; private Set myMapCheckMethods; @@ -57,25 +61,21 @@ public class InterceptorUserDataMapDstu2Test { @Before public void before() { - myInterceptor = mock(IServerInterceptor.class); - servlet.setInterceptors(Collections.singletonList(myInterceptor)); + servlet.getInterceptorService().unregisterAllInterceptors(); + servlet.getInterceptorService().registerInterceptor(new MyInterceptor()); } @Before public void beforePurgeMap() { myMap = null; - myMapCheckMethods= new LinkedHashSet(); + myMapCheckMethods= new LinkedHashSet<>(); } @Test public void testException() throws Exception { - IServerInterceptor interceptor = mock(IServerInterceptor.class, new MyInterceptorAnswer()); - - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=foo"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -87,25 +87,22 @@ public class InterceptorUserDataMapDstu2Test { @Test public void testRead() throws Exception { - IServerInterceptor interceptor = mock(IServerInterceptor.class, new MyInterceptorAnswer()); - - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - HttpResponse status = ourClient.execute(httpGet); - IOUtils.closeQuietly(status.getEntity().getContent()); + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { - for (int i = 0; i < 10; i++) { - if (!myMapCheckMethods.contains("processingCompletedNormally")) { - Thread.sleep(100); + for (int i = 0; i < 10; i++) { + if (!myMapCheckMethods.contains("processingCompletedNormally")) { + Thread.sleep(100); + } } + } ourLog.info(myMapCheckMethods.toString()); assertThat(myMapCheckMethods.toString(), myMapCheckMethods, contains("incomingRequestPostProcessed", "incomingRequestPreHandled", "outgoingResponse", "processingCompletedNormally")); } - protected void updateMapUsing(Map theUserData, Method theMethod) { + private void updateMapUsing(Map theUserData, String theMethod) { assertNotNull(theUserData); if (myMap == null) { myMap = theUserData; @@ -114,7 +111,7 @@ public class InterceptorUserDataMapDstu2Test { assertSame(myMap, theUserData); assertEquals(myValue, myMap.get(myKey)); } - myMapCheckMethods.add(theMethod.getName()); + myMapCheckMethods.add(theMethod); } @AfterClass @@ -236,24 +233,44 @@ public class InterceptorUserDataMapDstu2Test { } - private final class MyInterceptorAnswer implements Answer { - @Override - public Object answer(InvocationOnMock theInvocation) throws Throwable { - int index = 0; - for (Class next : theInvocation.getMethod().getParameterTypes()) { - if (RequestDetails.class.isAssignableFrom(next)) { - updateMapUsing(((RequestDetails)theInvocation.getArguments()[index]).getUserData(), theInvocation.getMethod()); - } - if (ActionRequestDetails.class.isAssignableFrom(next)) { - updateMapUsing(((ActionRequestDetails)theInvocation.getArguments()[index]).getUserData(), theInvocation.getMethod()); - } - index++; - } - if (theInvocation.getMethod().getReturnType().equals(boolean.class)) { - return true; - } - return null; + @Interceptor + public class MyInterceptor { + + @Hook(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED) + public void incomingRequestPostProcessed(RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) { + updateMapUsing(theRequestDetails.getUserData(), "incomingRequestPostProcessed"); + updateMapUsing(theServletRequestDetails.getUserData(), "incomingRequestPostProcessed"); } + + @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED) + public void incomingRequestPreHandled(ActionRequestDetails theRequestDetails) { + updateMapUsing(theRequestDetails.getUserData(), "incomingRequestPreHandled"); + } + + @Hook(Pointcut.SERVER_OUTGOING_RESPONSE) + public void outgoingResponse(RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) { + updateMapUsing(theRequestDetails.getUserData(), "outgoingResponse"); + updateMapUsing(theServletRequestDetails.getUserData(), "outgoingResponse"); + } + + @Hook(Pointcut.SERVER_PROCESSING_COMPLETED_NORMALLY) + public void processingCompletedNormally(RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) { + updateMapUsing(theRequestDetails.getUserData(), "processingCompletedNormally"); + updateMapUsing(theServletRequestDetails.getUserData(), "processingCompletedNormally"); + } + + @Hook(Pointcut.SERVER_PRE_PROCESS_OUTGOING_EXCEPTION) + public void preProcessOutgoingException(RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) { + updateMapUsing(theRequestDetails.getUserData(), "preProcessOutgoingException"); + updateMapUsing(theServletRequestDetails.getUserData(), "preProcessOutgoingException"); + } + + @Hook(Pointcut.SERVER_HANDLE_EXCEPTION) + public void handleException(RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) { + updateMapUsing(theRequestDetails.getUserData(), "handleException"); + updateMapUsing(theServletRequestDetails.getUserData(), "handleException"); + } + } public static class PlainProvider { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java index a72517521ef..cfe9c5f1176 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java @@ -1,18 +1,23 @@ package ca.uhn.fhir.rest.server.interceptor; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.matchesPattern; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; - -import java.util.*; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; +import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; +import ca.uhn.fhir.model.dstu2.resource.Bundle; +import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; +import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum; +import ca.uhn.fhir.model.primitive.DateDt; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.model.primitive.UriDt; +import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -26,26 +31,23 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.hamcrest.core.StringContains; -import org.junit.*; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; import org.mockito.ArgumentCaptor; import org.slf4j.Logger; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; -import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; -import ca.uhn.fhir.model.dstu2.resource.Bundle; -import ca.uhn.fhir.model.dstu2.resource.Patient; -import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; -import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum; -import ca.uhn.fhir.model.primitive.*; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; public class LoggingInterceptorDstu2Test { @@ -54,14 +56,12 @@ public class LoggingInterceptorDstu2Test { private static int ourPort; private static Server ourServer; private static RestfulServer servlet; - private IServerInterceptor myInterceptor; private static int ourDelayMs; private static Exception ourThrowException; @Before public void before() { - myInterceptor = mock(IServerInterceptor.class); - servlet.setInterceptors(Collections.singletonList(myInterceptor)); + servlet.getInterceptorService().unregisterAllInterceptors(); ourThrowException = null; ourDelayMs=0; } @@ -75,7 +75,7 @@ public class LoggingInterceptorDstu2Test { interceptor.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}"); assertEquals("ERROR - ${requestVerb} ${requestUrl}", interceptor.getErrorMessageFormat()); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -93,7 +93,7 @@ public class LoggingInterceptorDstu2Test { public void testMetadata() throws Exception { LoggingInterceptor interceptor = new LoggingInterceptor(); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -114,7 +114,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -136,7 +136,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -156,7 +156,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${processingTimeMillis}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -178,7 +178,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${processingTimeMillis}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -200,7 +200,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -221,7 +221,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -249,7 +249,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); interceptor.setErrorMessageFormat("ERROR - ${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -278,7 +278,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -299,7 +299,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -319,7 +319,7 @@ public class LoggingInterceptorDstu2Test { public void testRead() throws Exception { LoggingInterceptor interceptor = new LoggingInterceptor(); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -340,7 +340,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${idOrResourceName} - ${requestParameters}"); - servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor)); + servlet.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java deleted file mode 100644 index f9bec5d93d5..00000000000 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java +++ /dev/null @@ -1,2202 +0,0 @@ -package ca.uhn.fhir.rest.server.interceptor.auth; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import ca.uhn.fhir.rest.param.ReferenceParam; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.*; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.*; -import org.junit.Test; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.api.AddProfileTagEnum; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; -import ca.uhn.fhir.model.dstu2.resource.*; -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.annotation.*; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.IRequestOperationCallback; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.*; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; -import ca.uhn.fhir.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; - -public class AuthorizationInterceptorDstu2Test { - - private static final String ERR403 = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"error\",\"code\":\"processing\",\"diagnostics\":\"Access denied by default policy (no applicable rules)\"}]}"; - private static CloseableHttpClient ourClient; - private static String ourConditionalCreateId; - private static FhirContext ourCtx = FhirContext.forDstu2(); - private static boolean ourHitMethod; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AuthorizationInterceptorDstu2Test.class); - private static int ourPort; - private static List ourReturn; - private static Server ourServer; - private static RestfulServer ourServlet; - - @Before - public void before() { - ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER); - for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } - ourReturn = null; - ourHitMethod = false; - ourConditionalCreateId = "1123"; - } - - private IResource createCarePlan(Integer theId, String theSubjectId) { - CarePlan retVal = new CarePlan(); - if (theId != null) { - retVal.setId(new IdDt("CarePlan", (long) theId)); - } - retVal.setSubject(new ResourceReferenceDt("Patient/" + theSubjectId)); - return retVal; - } - - private HttpEntity createFhirResourceEntity(IBaseResource theResource) { - String out = ourCtx.newJsonParser().encodeResourceToString(theResource); - return new StringEntity(out, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")); - } - - private IResource createObservation(Integer theId, String theSubjectId) { - Observation retVal = new Observation(); - if (theId != null) { - retVal.setId(new IdDt("Observation", (long) theId)); - } - retVal.getCode().setText("OBS"); - retVal.setSubject(new ResourceReferenceDt(theSubjectId)); - return retVal; - } - - private IResource createPatient(Integer theId) { - Patient retVal = new Patient(); - if (theId != null) { - retVal.setId(new IdDt("Patient", (long) theId)); - } - retVal.addName().addFamily("FAM"); - return retVal; - } - - private IResource createPatient(Integer theId, int theVersion) { - IResource retVal = createPatient(theId); - retVal.setId(retVal.getId().withVersion(Integer.toString(theVersion))); - return retVal; - } - - private String extractResponseAndClose(HttpResponse status) throws IOException { - if (status.getEntity() == null) { - return null; - } - String responseContent; - responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - return responseContent; - } - - @Test - public void testAllowAll() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .deny("Rule 1").read().resourcesOfType(Patient.class).withAnyId().andThen() - .allowAll("Default Rule") - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Rule 1")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$validate"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - /** - * #528 - */ - @Test - public void testAllowByCompartmentWithAnyType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().allow("Rule 1").read().allResources().inCompartment("Patient", new IdDt("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().denyAll().build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "FOO")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - /** - * #528 - */ - @Test - public void testAllowByCompartmentWithType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().allow("Rule 1").read().resourcesOfType(CarePlan.class).inCompartment("Patient", new IdDt("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().denyAll() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "FOO")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testBatchWhenOnlyTransactionAllowed() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(BundleTypeEnum.BATCH); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(HTTPVerbEnum.POST); - - Bundle output = new Bundle(); - output.setType(BundleTypeEnum.TRANSACTION_RESPONSE); - output.addEntry().getResponse().setLocation("/Patient/1"); - - HttpPost httpPost; - HttpResponse status; - - ourReturn = Arrays.asList((IResource) output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - } - - @Test - public void testBatchWhenTransactionReadDenied() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(BundleTypeEnum.BATCH); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(HTTPVerbEnum.POST); - - Bundle output = new Bundle(); - output.setType(BundleTypeEnum.TRANSACTION_RESPONSE); - output.addEntry().setResource(createPatient(2)); - - HttpPost httpPost; - HttpResponse status; - - ourReturn = Arrays.asList((IResource) output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - } - - @Test - public void testBatchWhenTransactionWrongBundleType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(BundleTypeEnum.COLLECTION); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(HTTPVerbEnum.POST); - - Bundle output = new Bundle(); - output.setType(BundleTypeEnum.TRANSACTION_RESPONSE); - output.addEntry().setResource(createPatient(1)); - - HttpPost httpPost; - HttpResponse status; - String response; - - ourReturn = Arrays.asList((IResource) output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(422, status.getStatusLine().getStatusCode()); - } - - @Test - public void testDeleteByCompartment() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").delete().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").delete().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(1)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(204, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testDenyAll() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow().read().resourcesOfType(Patient.class).withAnyId().andThen() - .denyAll("Default Rule") - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Default Rule")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$validate"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Default Rule")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Default Rule")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - /** - * #528 - */ - @Test - public void testDenyByCompartmentWithAnyType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().deny("Rule 1").read().allResources().inCompartment("Patient", new IdDt("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().allowAll().build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "FOO")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - /** - * #528 - */ - @Test - public void testDenyByCompartmentWithType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().deny("Rule 1").read().resourcesOfType(CarePlan.class).inCompartment("Patient", new IdDt("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().allowAll() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Arrays.asList(createCarePlan(10, "FOO")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testHistoryWithReadAll() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().allResources().withAnyId() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Arrays.asList(createPatient(2, 1)); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testMetadataAllow() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").metadata() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Arrays.asList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - } - - @Test - public void testMetadataDeny() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.ALLOW) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .deny("Rule 1").metadata() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Arrays.asList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - } - - @Test - public void testOperationAnyName() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().withAnyName().onServer().andRequireExplicitResponseAuthorization().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - // Server - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testOperationByInstanceOfTypeAllowed() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = Arrays.asList(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Bundle")); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - ourReturn = Arrays.asList(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Encounter/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("OperationOutcome")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(false, ourHitMethod); - - } - - @Test - public void testOperationByInstanceOfTypeWithInvalidReturnValue() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // With a return value we don't allow - ourReturn = Arrays.asList(createPatient(222)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("OperationOutcome")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - // With a return value we do - ourReturn = Arrays.asList(createPatient(1)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Bundle")); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - } - - @Test - public void testOperationByInstanceOfTypeWithReturnValue() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = Arrays.asList(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Bundle")); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - ourReturn = Arrays.asList(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Encounter/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("OperationOutcome")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(false, ourHitMethod); - } - - @Test - public void testOperationInstanceLevel() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onInstance(new IdDt("http://example.com/Patient/1/_history/2")).andRequireExplicitResponseAuthorization().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testOperationInstanceLevelAnyInstance() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onAnyInstance().andRequireExplicitResponseAuthorization().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Another Instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/2/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong name - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$opName2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testOperationNotAllowedWithWritePermissiom() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - - return new RuleBuilder() - .allow("RULE 1").write().allResources().withAnyId().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // System - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testOperationServerLevel() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onServer().andRequireExplicitResponseAuthorization().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testOperationTypeLevel() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onType(Patient.class).andRequireExplicitResponseAuthorization().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Wrong name - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testOperationTypeLevelWildcard() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onAnyType().andRequireExplicitResponseAuthorization().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Another type - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong name - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testReadByAnyId() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).withAnyId() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = Arrays.asList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history/222"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(1), createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(2), createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testReadByCompartmentRight() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Arrays.asList(createPatient(1)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(1), createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testReadPageRight() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String respString; - Bundle respBundle; - - ourReturn = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - ourReturn.add(createObservation(i, "Patient/1")); - } - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_count=5&_format=json&subject=Patient/1"); - status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); - assertEquals(5, respBundle.getEntry().size()); - assertEquals(10, respBundle.getTotal().intValue()); - assertEquals("Observation/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); - assertNotNull(respBundle.getLink("next")); - - // Load next page - - ourHitMethod = false; - httpGet = new HttpGet(respBundle.getLink("next").getUrl()); - status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); - assertEquals(5, respBundle.getEntry().size()); - assertEquals(10, respBundle.getTotal().intValue()); - assertEquals("Observation/5", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); - assertNull(respBundle.getLink("next")); - - } - - @Test - public void testReadPageWrong() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String respString; - Bundle respBundle; - - ourReturn = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - ourReturn.add(createObservation(i, "Patient/1")); - } - for (int i = 5; i < 10; i++) { - ourReturn.add(createObservation(i, "Patient/2")); - } - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_count=5&_format=json&subject=Patient/1"); - status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); - assertEquals(5, respBundle.getEntry().size()); - assertEquals(10, respBundle.getTotal().intValue()); - assertEquals("Observation/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); - assertNotNull(respBundle.getLink("next")); - - // Load next page - - ourHitMethod = false; - httpGet = new HttpGet(respBundle.getLink("next").getUrl()); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testReadByCompartmentWrong() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = Collections.singletonList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createCarePlan(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(1), createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(2), createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testTransactionWriteGood() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(BundleTypeEnum.TRANSACTION); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(HTTPVerbEnum.PUT); - - Bundle output = new Bundle(); - output.setType(BundleTypeEnum.TRANSACTION_RESPONSE); - output.addEntry().getResponse().setLocation("/Patient/1"); - - HttpPost httpPost; - HttpResponse status; - - ourReturn = Collections.singletonList(output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - } - - @Test - public void testWriteByCompartmentCreate() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 1b").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1123")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - // Conditional - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.addHeader("If-None-Exist", "Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/2"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/1"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - ourLog.debug(response); - assertEquals(201, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testWriteByCompartmentCreateConditionalResolvesToValid() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").createConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(201, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentDeleteConditionalResolvesToValid() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").delete().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").deleteConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(1)); - - ourHitMethod = false; - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar"); - status = ourClient.execute(httpDelete); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(204, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentDeleteConditionalWithoutDirectMatch() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 2").deleteConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(1)); - - ourHitMethod = false; - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar"); - status = ourClient.execute(httpDelete); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentDoesntAllowDelete() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(1)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testWriteByCompartmentUpdate() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - String response; - HttpResponse status; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); - httpPost.setEntity(createFhirResourceEntity(createPatient(2))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/1"); - httpPost.setEntity(createFhirResourceEntity(createPatient(1))); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Conditional - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(1))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(99))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/10"); - httpPost.setEntity(createFhirResourceEntity(createObservation(10, "Patient/1"))); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/10"); - httpPost.setEntity(createFhirResourceEntity(createObservation(10, "Patient/2"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testWriteByCompartmentUpdateConditionalResolvesToInvalid() throws Exception { - ourConditionalCreateId = "1123"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 3").updateConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentUpdateConditionalResolvesToValid() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 3").updateConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - ourLog.debug(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentUpdateConditionalResolvesToValidAllTypes() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() - .allow("Rule 3").updateConditional().allResources() - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - ourLog.debug(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertTrue(ourHitMethod); - - } - - @Test - public void testInvalidInstanceIds() { - try { - new RuleBuilder().allow("Rule 1").write().instance((String) null); - fail(); - } catch (NullPointerException e) { - assertEquals("theId must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(""); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("theId must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance("Observation/"); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("theId must contain an ID part", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(new IdDt()); - fail(); - } catch (NullPointerException e) { - assertEquals("theId.getValue() must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(new IdDt("")); - fail(); - } catch (NullPointerException e) { - assertEquals("theId.getValue() must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(new IdDt("Observation", (String) null)); - fail(); - } catch (NullPointerException e) { - assertEquals("theId must contain an ID part", e.getMessage()); - } - } - - @Test - public void testWritePatchByInstance() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow().patch().allRequests().andThen() - .allow("Rule 1").write().instance("Patient/900").andThen() - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - String input = "[ { \"op\": \"replace\", \"path\": \"/gender\", \"value\": \"male\" } ]"; - - ourHitMethod = false; - httpPost = new HttpPatch("http://localhost:" + ourPort + "/Patient/900"); - httpPost.setEntity(new StringEntity(input, ContentType.parse("application/json-patch+json"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByInstance() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().instance("Observation/900").andThen() - .allow("Rule 1").write().instance("901").andThen() - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/900"); - httpPost.setEntity(createFhirResourceEntity(createObservation(900, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/901"); - httpPost.setEntity(createFhirResourceEntity(createObservation(901, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/900"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - } - - @Test - public void testReadByInstance() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().instance("Observation/900").andThen() - .allow("Rule 1").read().instance("901").andThen() - .build(); - } - }); - - HttpResponse status; - String response; - HttpGet httpGet; - - ourReturn = Arrays.asList(createObservation(900, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/900"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(901)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/901"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(1)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=json"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - } - - @AfterClass - public static void afterClassClearContext() throws Exception { - ourServer.stop(); - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @BeforeClass - public static void beforeClass() throws Exception { - - ourPort = PortUtil.findFreePort(); - ourServer = new Server(ourPort); - - DummyPatientResourceProvider patProvider = new DummyPatientResourceProvider(); - DummyObservationResourceProvider obsProv = new DummyObservationResourceProvider(); - DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider(); - DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider(); - PlainProvider plainProvider = new PlainProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv); - ourServlet.setPlainProviders(plainProvider); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100)); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - ourServer.start(); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyCarePlanResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return CarePlan.class; - } - - @Read(version = true) - public CarePlan read(@IdParam IdDt theId) { - ourHitMethod = true; - return (CarePlan) ourReturn.get(0); - } - - @Search() - public List search() { - ourHitMethod = true; - return ourReturn; - } - } - - public static class DummyEncounterResourceProvider implements IResourceProvider { - - @Operation(name = "everything", idempotent = true) - public Bundle everything(@IdParam IdDt theId) { - ourHitMethod = true; - Bundle retVal = new Bundle(); - for (IResource next : ourReturn) { - retVal.addEntry().setResource(next); - } - return retVal; - } - - @Override - public Class getResourceType() { - return Encounter.class; - } - } - - public static class DummyObservationResourceProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Observation theResource, @ConditionalUrlParam String theConditionalUrl) { - ourHitMethod = true; - theResource.setId("Observation/1/_history/1"); - MethodOutcome retVal = new MethodOutcome(); - retVal.setCreated(true); - retVal.setResource(theResource); - return retVal; - } - - @Delete() - public MethodOutcome delete(@IdParam IdDt theId) { - ourHitMethod = true; - MethodOutcome retVal = new MethodOutcome(); - return retVal; - } - - @Override - public Class getResourceType() { - return Observation.class; - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation(@IdParam IdDt theId) { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Read(version = true) - public Observation read(@IdParam IdDt theId) { - ourHitMethod = true; - return (Observation) ourReturn.get(0); - } - - @Search() - public List search(@OptionalParam(name="subject")ReferenceParam theSubject) { - ourHitMethod = true; - return ourReturn; - } - - @Update() - public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Observation theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - ourHitMethod = true; - - if (isNotBlank(theConditionalUrl)) { - IdDt actual = new IdDt("Observation", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(actual); - } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(theId.withVersion("2")); - } - - MethodOutcome retVal = new MethodOutcome(); - retVal.setResource(theResource); - return retVal; - } - - } - - @SuppressWarnings("unused") - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - - if (isNotBlank(theConditionalUrl)) { - IdDt actual = new IdDt("Patient", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.CREATE); - } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.CREATE); - } - - ourHitMethod = true; - theResource.setId("Patient/1/_history/1"); - MethodOutcome retVal = new MethodOutcome(); - retVal.setCreated(true); - retVal.setResource(theResource); - return retVal; - } - - @Delete() - public MethodOutcome delete(IRequestOperationCallback theRequestOperationCallback, @IdParam IdDt theId, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - ourHitMethod = true; - for (IBaseResource next : ourReturn) { - theRequestOperationCallback.resourceDeleted(next); - } - MethodOutcome retVal = new MethodOutcome(); - return retVal; - } - - @Operation(name = "everything", idempotent = true) - public Bundle everything(@IdParam IdDt theId) { - ourHitMethod = true; - Bundle retVal = new Bundle(); - for (IResource next : ourReturn) { - retVal.addEntry().setResource(next); - } - return retVal; - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @History() - public List history() { - ourHitMethod = true; - return (ourReturn); - } - - @History() - public List history(@IdParam IdDt theId) { - ourHitMethod = true; - return (ourReturn); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation(@IdParam IdDt theId) { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName2", idempotent = true) - public Parameters operation2(@IdParam IdDt theId) { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName2", idempotent = true) - public Parameters operation2() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Read(version = true) - public Patient read(@IdParam IdDt theId) { - ourHitMethod = true; - return (Patient) ourReturn.get(0); - } - - @Search() - public List search() { - ourHitMethod = true; - return ourReturn; - } - - @Update() - public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - ourHitMethod = true; - - if (isNotBlank(theConditionalUrl)) { - IdDt actual = new IdDt("Patient", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(actual); - } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(theId.withVersion("2")); - } - - MethodOutcome retVal = new MethodOutcome(); - retVal.setResource(theResource); - return retVal; - } - - @Patch() - public MethodOutcome patch(@IdParam IdDt theId, @ResourceParam String theResource, PatchTypeEnum thePatchType) { - ourHitMethod = true; - - MethodOutcome retVal = new MethodOutcome(); - return retVal; - } - - @Validate - public MethodOutcome validate(@ResourceParam Patient theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, - @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile, RequestDetails theRequestDetails) { - ourHitMethod = true; - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics("OK"); - return new MethodOutcome(oo); - } - - @Validate - public MethodOutcome validate(@ResourceParam Patient theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode, - @Validate.Profile String theProfile, RequestDetails theRequestDetails) { - ourHitMethod = true; - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics("OK"); - return new MethodOutcome(oo); - } - - } - - public static class PlainProvider { - - @History() - public List history() { - ourHitMethod = true; - return (ourReturn); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Transaction() - public Bundle search(@TransactionParam Bundle theInput) { - ourHitMethod = true; - return (Bundle) ourReturn.get(0); - } - - } - -} diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java index 3b7428cb0c6..4cfea1552b5 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/IdType.java @@ -220,11 +220,11 @@ public final class IdType extends UriType implements IPrimitiveType, IId setValue(theUrl.getValueAsString()); } - public void applyTo(IBaseResource theResouce) { - if (theResouce == null) { + public void applyTo(IBaseResource theResource) { + if (theResource == null) { throw new NullPointerException("theResource can not be null"); } else { - theResouce.setId(new IdType(getValue())); + theResource.setId(new IdType(getValue())); } } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/InstantType.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/InstantType.java index 3ce6c865569..414ee57cc3a 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/InstantType.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/InstantType.java @@ -156,7 +156,9 @@ public class InstantType extends BaseDateTimeType { @Override public InstantType copy() { - return new InstantType(getValueAsString()); + InstantType retVal = new InstantType(); + retVal.setValueAsString(getValueAsString()); + return retVal; } public String fhirType() { diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/UriType.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/UriType.java index 255f38ee56a..7c58955d59e 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/UriType.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/UriType.java @@ -1,163 +1,165 @@ -/* -Copyright (c) 2011+, HL7, Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ -package org.hl7.fhir.dstu3.model; - -import java.net.URI; -import java.net.URISyntaxException; - -import org.apache.commons.lang3.StringUtils; - -import ca.uhn.fhir.model.api.annotation.DatatypeDef; - -/** - * Primitive type "uri" in FHIR: any valid URI. Sometimes constrained to be only an absolute URI, and sometimes constrained to be a literal reference - */ -@DatatypeDef(name = "uri") -public class UriType extends PrimitiveType { - - private static final long serialVersionUID = 3L; - - /** - * Constructor - */ - public UriType() { - // nothing - } - - /** - * Constructor - */ - public UriType(String theValue) { - setValueAsString(theValue); - } - - /** - * Constructor - */ - public UriType(URI theValue) { - setValue(theValue.toString()); - } - - @Override - public UriType copy() { - return new UriType(getValue()); - } - - @Override - protected String encode(String theValue) { - return theValue; - } - - /** - * Compares the given string to the string representation of this URI. In many cases it is preferable to use this - * instead of the standard {@link #equals(Object)} method, since that method returns false unless it is - * passed an instance of {@link UriType} - */ - public boolean equals(String theString) { - return StringUtils.equals(getValueAsString(), theString); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - String normalize = normalize(getValue()); - result = prime * result + ((normalize == null) ? 0 : normalize.hashCode()); - - return result; - } - - private String normalize(String theValue) { - if (theValue == null) { - return null; - } - try { - URI retVal = new URI(getValue()).normalize(); - String urlString = retVal.toString(); - if (urlString.endsWith("/") && urlString.length() > 1) { - retVal = new URI(urlString.substring(0, urlString.length() - 1)); - } - return retVal.toASCIIString(); - } catch (URISyntaxException e) { - // ourLog.debug("Failed to normalize URL '{}', message was: {}", urlString, e.toString()); - return theValue; - } - } - - @Override - protected String parse(String theValue) { - return theValue; - } - - /** - * Creates a new OidType instance which uses the given OID as the content (and prepends "urn:oid:" to the OID string - * in the value of the newly created OidType, per the FHIR specification). - * - * @param theOid - * The OID to use (null is acceptable and will result in a UriDt instance with a - * null value) - * @return A new UriDt instance - */ - public static OidType fromOid(String theOid) { - if (theOid == null) { - return new OidType(); - } - return new OidType("urn:oid:" + theOid); - } - - @Override - public boolean equalsDeep(Base obj) { - if (!super.equalsDeep(obj)) - return false; - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - - UriType other = (UriType) obj; - if (getValue() == null && other.getValue() == null) { - return true; - } - if (getValue() == null || other.getValue() == null) { - return false; - } - - String normalize = normalize(getValue()); - String normalize2 = normalize(other.getValue()); - return normalize.equals(normalize2); - } - - public String fhirType() { - return "uri"; - } - -} +/* +Copyright (c) 2011+, HL7, Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ +package org.hl7.fhir.dstu3.model; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.commons.lang3.StringUtils; + +import ca.uhn.fhir.model.api.annotation.DatatypeDef; + +import static org.apache.commons.lang3.StringUtils.defaultString; + +/** + * Primitive type "uri" in FHIR: any valid URI. Sometimes constrained to be only an absolute URI, and sometimes constrained to be a literal reference + */ +@DatatypeDef(name = "uri") +public class UriType extends PrimitiveType { + + private static final long serialVersionUID = 3L; + + /** + * Constructor + */ + public UriType() { + // nothing + } + + /** + * Constructor + */ + public UriType(String theValue) { + setValueAsString(theValue); + } + + /** + * Constructor + */ + public UriType(URI theValue) { + setValue(theValue.toString()); + } + + @Override + public UriType copy() { + return new UriType(getValue()); + } + + @Override + protected String encode(String theValue) { + return theValue; + } + + /** + * Compares the given string to the string representation of this URI. In many cases it is preferable to use this + * instead of the standard {@link #equals(Object)} method, since that method returns false unless it is + * passed an instance of {@link UriType} + */ + public boolean equals(String theString) { + return StringUtils.equals(getValueAsString(), theString); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + String normalize = normalize(getValue()); + result = prime * result + ((normalize == null) ? 0 : normalize.hashCode()); + + return result; + } + + private String normalize(String theValue) { + if (theValue == null) { + return null; + } + try { + URI retVal = new URI(getValue()).normalize(); + String urlString = retVal.toString(); + if (urlString.endsWith("/") && urlString.length() > 1) { + retVal = new URI(urlString.substring(0, urlString.length() - 1)); + } + return retVal.toASCIIString(); + } catch (URISyntaxException e) { + // ourLog.debug("Failed to normalize URL '{}', message was: {}", urlString, e.toString()); + return theValue; + } + } + + @Override + protected String parse(String theValue) { + return theValue; + } + + /** + * Creates a new OidType instance which uses the given OID as the content (and prepends "urn:oid:" to the OID string + * in the value of the newly created OidType, per the FHIR specification). + * + * @param theOid + * The OID to use (null is acceptable and will result in a UriDt instance with a + * null value) + * @return A new UriDt instance + */ + public static OidType fromOid(String theOid) { + if (theOid == null) { + return new OidType(); + } + return new OidType("urn:oid:" + theOid); + } + + @Override + public boolean equalsDeep(Base obj) { + if (!super.equalsDeep(obj)) + return false; + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + UriType other = (UriType) obj; + if (getValue() == null && other.getValue() == null) { + return true; + } + if (getValue() == null || other.getValue() == null) { + return false; + } + + String normalize = normalize(getValue()); + String normalize2 = normalize(other.getValue()); + return normalize.equals(normalize2); + } + + public String fhirType() { + return "uri"; + } + +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java index bcd36c10200..1d07467043f 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java @@ -69,6 +69,16 @@ public class XmlParserDstu3Test { ourCtx.setNarrativeGenerator(null); } + @Test + public void testEncodeInvalidMetaTime() { + + Patient p = new Patient(); + p.getMeta().getLastUpdatedElement().setValueAsString("2019-01-01"); + String output = ourCtx.newXmlParser().encodeResourceToString(p); + assertThat(output, containsString("lastUpdated value=\"2019-01-01\"")); + + } + @Test public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() { String refVal = "http://my.org/FooBar"; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java index f5cb31acf6b..fabfa4cc0ee 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.*; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; @@ -58,9 +59,7 @@ public class InterceptorDstu3Test { @After public void after() { - for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); } @Before @@ -116,12 +115,19 @@ public class InterceptorDstu3Test { when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(IBaseResource.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor2.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor2.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor2.outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); - when(myInterceptor2.outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor2.outgoingResponse(nullable(RequestDetails.class))).thenReturn(true); + when(myInterceptor2.outgoingResponse(nullable(RequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); + when(myInterceptor2.outgoingResponse(nullable(RequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor2.outgoingResponse(nullable(RequestDetails.class), nullable(IBaseResource.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor2.outgoingResponse(nullable(RequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); String input = createInput(); @@ -139,18 +145,6 @@ public class InterceptorDstu3Test { ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture()); order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(nullable(RestOperationTypeEnum.class), nullable(ActionRequestDetails.class)); - order.verify(myInterceptor2, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class)); - order.verify(myInterceptor2, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - order.verify(myInterceptor1, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class)); - order.verify(myInterceptor1, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); - - // Avoid concurrency issues - Thread.sleep(500); - - order.verify(myInterceptor2, times(1)).processingCompletedNormally(nullable(ServletRequestDetails.class)); - order.verify(myInterceptor1, times(1)).processingCompletedNormally(nullable(ServletRequestDetails.class)); - verifyNoMoreInteractions(myInterceptor1); - verifyNoMoreInteractions(myInterceptor2); assertEquals(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, opTypeCapt.getValue()); assertNotNull(arTypeCapt.getValue().getResource()); @@ -162,8 +156,11 @@ public class InterceptorDstu3Test { when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(IBaseResource.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); doThrow(new NullPointerException("FOO")).when(myInterceptor1).processingCompletedNormally(any()); String input = createInput(); @@ -184,8 +181,11 @@ public class InterceptorDstu3Test { when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(IBaseResource.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); String input = createInput(); @@ -219,7 +219,11 @@ public class InterceptorDstu3Test { when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor1.outgoingResponse(nullable(ServletRequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(OperationOutcome.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(ResponseDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(IBaseResource.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor1.outgoingResponse(nullable(RequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); String input = createInput(); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java index 8d21d6a1caf..a3ebc15b981 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java @@ -66,10 +66,7 @@ public class SearchPostDstu3Test { ourLastMethod = null; ourLastSortSpec = null; ourLastName = null; - - for (IServerInterceptor next : new ArrayList(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); } /** diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorDstu3Test.java index f3fe6b070de..772d9f3fa91 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorDstu3Test.java @@ -2,11 +2,13 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.AddProfileTagEnum; +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.primitive.IdDt; import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.IRequestOperationCallback; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenAndListParam; @@ -16,6 +18,7 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.auth.*; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; @@ -69,9 +72,7 @@ public class AuthorizationInterceptorDstu3Test { @Before public void before() { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER); - for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); ourServlet.setTenantIdentificationStrategy(null); ourReturn = null; ourDeleted = null; @@ -3478,10 +3479,14 @@ public class AuthorizationInterceptorDstu3Test { } @Delete() - public MethodOutcome delete(IRequestOperationCallback theRequestOperationCallback, @IdParam IdType theId, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { + public MethodOutcome delete(IInterceptorBroadcaster theRequestOperationCallback, @IdParam IdType theId, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { ourHitMethod = true; for (IBaseResource next : ourReturn) { - theRequestOperationCallback.resourceDeleted(next); + HookParams params = new HookParams() + .add(IBaseResource.class, next) + .add(RequestDetails.class, theRequestDetails) + .addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + theRequestOperationCallback.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, params); } return new MethodOutcome(); } @@ -3626,11 +3631,15 @@ public class AuthorizationInterceptorDstu3Test { @Transaction() - public Bundle search(IRequestOperationCallback theRequestOperationCallback, @TransactionParam Bundle theInput) { + public Bundle search(ServletRequestDetails theRequestDetails, IInterceptorBroadcaster theInterceptorBroadcaster, @TransactionParam Bundle theInput) { ourHitMethod = true; if (ourDeleted != null) { for (IBaseResource next : ourDeleted) { - theRequestOperationCallback.resourceDeleted(next); + HookParams params = new HookParams() + .add(IBaseResource.class, next) + .add(RequestDetails.class, theRequestDetails) + .add(ServletRequestDetails.class, theRequestDetails); + theInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED, params); } } return (Bundle) ourReturn.get(0); diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java index cd5390eba58..0e9bcbbbae1 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseDateTimeType.java @@ -869,18 +869,33 @@ public abstract class BaseDateTimeType extends PrimitiveType { *
  • true if the given datetimes represent the exact same instant with the same precision (irrespective of the timezone)
  • *
  • true if the given datetimes represent the exact same instant but one includes milliseconds of .[0]+ while the other includes only SECONDS precision (irrespecitve of the timezone)
  • *
  • true if the given datetimes represent the exact same year/year-month/year-month-date (if both operands have the same precision)
  • - *
  • false if both datetimes have equal precision of MINUTE or greater, one has no timezone specified but the other does, and could not represent the same instant in any timezone
  • - *
  • null if both datetimes have equal precision of MINUTE or greater, one has no timezone specified but the other does, and could potentially represent the same instant in any timezone
  • - *
  • false if the given datetimes have the same precision but do not represent the same instant (irrespective of timezone)
  • + *
  • false if both datetimes have equal precision of MINUTE or greater, one has no timezone specified but the other does, and could not represent the same instant in any timezone
  • + *
  • null if both datetimes have equal precision of MINUTE or greater, one has no timezone specified but the other does, and could potentially represent the same instant in any timezone
  • + *
  • false if the given datetimes have the same precision but do not represent the same instant (irrespective of timezone)
  • *
  • null otherwise (since these datetimes are not comparable)
  • * */ public Boolean equalsUsingFhirPathRules(BaseDateTimeType theOther) { - if (hasTimezoneIfRequired() != theOther.hasTimezoneIfRequired()) { - if (getPrecision() == theOther.getPrecision()) { - if (getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal() && theOther.getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal()) { - boolean couldBeTheSameTime = couldBeTheSameTime(this, theOther) || couldBeTheSameTime(theOther, this); + BaseDateTimeType me = this; + + // Per FHIRPath rules, we compare equivalence at the lowest precision of the two values, + // so if we need to, we'll clone either side and reduce its precision + int lowestPrecision = Math.min(me.getPrecision().ordinal(), theOther.getPrecision().ordinal()); + TemporalPrecisionEnum lowestPrecisionEnum = TemporalPrecisionEnum.values()[lowestPrecision]; + if (me.getPrecision() != lowestPrecisionEnum) { + me = new DateTimeType(me.getValueAsString()); + me.setPrecision(lowestPrecisionEnum); + } + if (theOther.getPrecision() != lowestPrecisionEnum) { + theOther = new DateTimeType(theOther.getValueAsString()); + theOther.setPrecision(lowestPrecisionEnum); + } + + if (me.hasTimezoneIfRequired() != theOther.hasTimezoneIfRequired()) { + if (me.getPrecision() == theOther.getPrecision()) { + if (me.getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal() && theOther.getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal()) { + boolean couldBeTheSameTime = couldBeTheSameTime(me, theOther) || couldBeTheSameTime(theOther, me); if (!couldBeTheSameTime) { return false; } @@ -890,16 +905,24 @@ public abstract class BaseDateTimeType extends PrimitiveType { } // Same precision - if (getPrecision() == theOther.getPrecision()) { - return getValue().getTime() == theOther.getValue().getTime(); + if (me.getPrecision() == theOther.getPrecision()) { + if (me.getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal()) { + long leftTime = me.getValue().getTime(); + long rightTime = theOther.getValue().getTime(); + return leftTime == rightTime; + } else { + String leftTime = me.getValueAsString(); + String rightTime = theOther.getValueAsString(); + return leftTime.equals(rightTime); + } } // Both represent 0 millis but the millis are optional - if (((Integer)0).equals(getMillis())) { + if (((Integer)0).equals(me.getMillis())) { if (((Integer)0).equals(theOther.getMillis())) { - if (getPrecision().ordinal() >= TemporalPrecisionEnum.SECOND.ordinal()) { + if (me.getPrecision().ordinal() >= TemporalPrecisionEnum.SECOND.ordinal()) { if (theOther.getPrecision().ordinal() >= TemporalPrecisionEnum.SECOND.ordinal()) { - return getValue().getTime() == theOther.getValue().getTime(); + return me.getValue().getTime() == theOther.getValue().getTime(); } } } diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/InstantType.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/InstantType.java index b4af9702efc..648ae9b510c 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/InstantType.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/InstantType.java @@ -198,7 +198,8 @@ public class InstantType extends BaseDateTimeType { @Override public InstantType copy() { - InstantType ret = new InstantType(getValueAsString()); + InstantType ret = new InstantType(); + ret.setValueAsString(getValueAsString()); copyValues(ret); return ret; } diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java index d059ad6a986..be3c88c9a63 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java @@ -1436,13 +1436,15 @@ public class FHIRPathEngine { } private List opAs(List left, List right) { - List result = new ArrayList(); - if (left.size() != 1 || right.size() != 1) + List result = new ArrayList<>(); + if (right.size() != 1) return result; else { String tn = convertToString(right); - if (tn.equals(left.get(0).fhirType())) - result.add(left.get(0)); + for (Base nextLeft : left) { + if (tn.equals(nextLeft.fhirType())) + result.add(nextLeft); + } } return result; } @@ -3298,7 +3300,7 @@ public class FHIRPathEngine { if (s.startsWith("#")) { Property p = context.resource.getChildByName("contained"); for (Base c : p.getValues()) { - if (s.substring(1).equals(c.getIdBase())) { + if (chompHash(s).equals(chompHash(c.getIdBase()))) { res = c; break; } @@ -3314,7 +3316,18 @@ public class FHIRPathEngine { return result; } - private List funcExtension(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { + /** + * Strips a leading hashmark (#) if present at the start of a string + */ + private String chompHash(String theId) { + String retVal = theId; + while (retVal.startsWith("#")) { + retVal = retVal.substring(1); + } + return retVal; + } + + private List funcExtension(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List result = new ArrayList(); List nl = execute(context, focus, exp.getParameters().get(0), true); String url = nl.get(0).primitiveValue(); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java index ac72ac9fe7c..4550967ddbb 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java @@ -1,31 +1,5 @@ package ca.uhn.fhir.rest.server.interceptor; -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.r4.model.Patient; -import org.junit.*; -import org.mockito.ArgumentCaptor; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -35,6 +9,28 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; +import com.google.common.base.Charsets; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.hl7.fhir.r4.model.Patient; +import org.junit.*; +import org.mockito.ArgumentCaptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; public class ExceptionInterceptorMethodTest { @@ -49,9 +45,13 @@ public class ExceptionInterceptorMethodTest { @Before public void before() { myInterceptor = mock(IServerInterceptor.class); - servlet.setInterceptors(Collections.singletonList(myInterceptor)); + servlet.getInterceptorService().registerInterceptor(myInterceptor); + } + + @After + public void after() { + servlet.getInterceptorService().unregisterInterceptor(myInterceptor); } - @Test public void testThrowUnprocessableEntityException() throws Exception { @@ -61,10 +61,10 @@ public class ExceptionInterceptorMethodTest { when(myInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException"); - HttpResponse status = ourClient.execute(httpGet); - ourLog.info(IOUtils.toString(status.getEntity().getContent())); - assertEquals(422, status.getStatusLine().getStatusCode()); - IOUtils.closeQuietly(status.getEntity().getContent()); + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { + ourLog.info(IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8)); + assertEquals(422, status.getStatusLine().getStatusCode()); + } ArgumentCaptor captor = ArgumentCaptor.forClass(BaseServerResponseException.class); verify(myInterceptor, times(1)).handleException(any(RequestDetails.class), captor.capture(), any(HttpServletRequest.class), any(HttpServletResponse.class)); @@ -78,27 +78,23 @@ public class ExceptionInterceptorMethodTest { when(myInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(myInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenAnswer(new Answer() { - @Override - public Boolean answer(InvocationOnMock theInvocation) throws Throwable { - HttpServletResponse resp = (HttpServletResponse) theInvocation.getArguments()[3]; - resp.setStatus(405); - resp.setContentType("text/plain"); - resp.getWriter().write("HELP IM A BUG"); - resp.getWriter().close(); - return false; - } + when(myInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenAnswer(theInvocation -> { + HttpServletResponse resp = (HttpServletResponse) theInvocation.getArguments()[3]; + resp.setStatus(405); + resp.setContentType("text/plain"); + resp.getWriter().write("HELP IM A BUG"); + resp.getWriter().close(); + return false; }); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(405, status.getStatusLine().getStatusCode()); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals("HELP IM A BUG", responseContent); - + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(responseContent); + assertEquals(405, status.getStatusLine().getStatusCode()); + assertEquals("HELP IM A BUG", responseContent); + } + } @AfterClass @@ -129,9 +125,6 @@ public class ExceptionInterceptorMethodTest { } - /** - * Created by dsotnikov on 2/25/2014. - */ public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -142,8 +135,6 @@ public class ExceptionInterceptorMethodTest { /** * Retrieve the resource by its identifier * - * @param theId - * The resource identity * @return The resource */ @Search(queryName = "throwUnprocessableEntityException") diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorThrowingExceptionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorThrowingExceptionR4Test.java index 49f140c777c..0ff8eb86fb5 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorThrowingExceptionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorThrowingExceptionR4Test.java @@ -4,23 +4,14 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.AddProfileTagEnum; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; -import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; -import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; -import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; -import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -28,15 +19,16 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.*; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Resource; +import org.junit.*; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.concurrent.TimeUnit; import static org.hamcrest.CoreMatchers.containsString; @@ -45,10 +37,8 @@ import static org.junit.Assert.*; public class InterceptorThrowingExceptionR4Test { - private static final String ERR403 = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"error\",\"code\":\"processing\",\"diagnostics\":\"Access denied by default policy (no applicable rules)\"}]}"; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InterceptorThrowingExceptionR4Test.class); private static CloseableHttpClient ourClient; - private static String ourConditionalCreateId; private static FhirContext ourCtx = FhirContext.forR4(); private static boolean ourHitMethod; private static int ourPort; @@ -59,13 +49,15 @@ public class InterceptorThrowingExceptionR4Test { @Before public void before() { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER); - for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); ourServlet.setTenantIdentificationStrategy(null); ourReturn = null; ourHitMethod = false; - ourConditionalCreateId = "1123"; + } + + @After + public void after(){ + ourServlet.getInterceptorService().unregisterAllInterceptors(); } @@ -85,28 +77,28 @@ public class InterceptorThrowingExceptionR4Test { } String responseContent; responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); + status.getEntity().getContent().close(); return responseContent; } @Test public void testFailureInProcessingCompletedNormally() throws Exception { final List hit = new ArrayList<>(); - ourServlet.registerInterceptor(new InterceptorAdapter(){ + ourServlet.getInterceptorService().registerInterceptor(new InterceptorAdapter() { @Override public void processingCompletedNormally(ServletRequestDetails theRequestDetails) { hit.add(1); throw new NullPointerException(); } }); - ourServlet.registerInterceptor(new InterceptorAdapter(){ + ourServlet.getInterceptorService().registerInterceptor(new InterceptorAdapter() { @Override public void processingCompletedNormally(ServletRequestDetails theRequestDetails) { hit.add(2); throw new NullPointerException(); } }); - ourServlet.registerInterceptor(new InterceptorAdapter(){ + ourServlet.getInterceptorService().registerInterceptor(new InterceptorAdapter() { @Override public void processingCompletedNormally(ServletRequestDetails theRequestDetails) { hit.add(3); @@ -127,10 +119,26 @@ public class InterceptorThrowingExceptionR4Test { assertThat(response, containsString("FAM")); assertTrue(ourHitMethod); ourLog.info("Hit: {}", hit); - assertThat(hit, contains(3,2,1)); + assertThat("Hits: " + hit.toString(), hit, contains(1, 2, 3)); } + public static class DummyPatientResourceProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + + @Read(version = true) + public Patient read(@IdParam IdType theId) { + ourHitMethod = true; + return (Patient) ourReturn.get(0); + } + + + } @AfterClass public static void afterClassClearContext() throws Exception { @@ -167,23 +175,4 @@ public class InterceptorThrowingExceptionR4Test { } - public static class DummyPatientResourceProvider implements IResourceProvider { - - - @Override - public Class getResourceType() { - return Patient.class; - } - - - @Read(version = true) - public Patient read(@IdParam IdType theId) { - ourHitMethod = true; - return (Patient) ourReturn.get(0); - } - - - } - - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java index d22aa144684..62eba17ddea 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.BundleInclusionRule; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; @@ -155,7 +156,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); HashMap params = new HashMap<>(); reqDetails.setParameters(params); @@ -381,7 +382,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); reqDetails.setServer(new RestfulServer(ourCtx)); reqDetails.setServletRequest(req); @@ -416,7 +417,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); HashMap params = new HashMap<>(); params.put(Constants.PARAM_FORMAT, new String[]{Constants.FORMAT_HTML}); @@ -445,7 +446,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); HashMap params = new HashMap<>(); params.put(Constants.PARAM_FORMAT, new String[]{Constants.CT_HTML}); @@ -471,7 +472,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); HashMap params = new HashMap<>(); params.put(Constants.PARAM_PRETTY, new String[]{Constants.PARAM_PRETTY_VALUE_TRUE}); @@ -500,7 +501,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); reqDetails.setParameters(new HashMap<>()); reqDetails.setServer(new RestfulServer(ourCtx)); @@ -529,7 +530,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); HashMap params = new HashMap<>(); params.put(Constants.PARAM_PRETTY, new String[]{Constants.PARAM_PRETTY_VALUE_TRUE}); @@ -563,7 +564,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); reqDetails.setParameters(new HashMap<>()); RestfulServer server = new RestfulServer(ourCtx); @@ -593,7 +594,7 @@ public class ResponseHighlightingInterceptorTest { Patient resource = new Patient(); resource.addName().setFamily("FAMILY"); - ServletRequestDetails reqDetails = new TestServletRequestDetails(); + ServletRequestDetails reqDetails = new TestServletRequestDetails(mock(IInterceptorBroadcaster.class)); reqDetails.setRequestType(RequestTypeEnum.GET); reqDetails.setParameters(new HashMap<>()); RestfulServer server = new RestfulServer(ourCtx); @@ -789,6 +790,10 @@ public class ResponseHighlightingInterceptorTest { } class TestServletRequestDetails extends ServletRequestDetails { + TestServletRequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) { + super(theInterceptorBroadcaster); + } + @Override public String getServerBaseForRequest() { return "/baseDstu3"; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProviderTest.java index 121c5a55abd..df0faea3962 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProviderTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProviderTest.java @@ -205,6 +205,7 @@ public class HashMapResourceProviderTest { for (int i = 0; i < 100; i++) { Patient p = new Patient(); p.addName().setFamily("FAM" + i); + ourClient.registerInterceptor(new LoggingInterceptor(true)); IIdType id = ourClient.create().resource(p).execute().getId(); assertThat(id.getIdPart(), matchesPattern("[0-9]+")); assertEquals("1", id.getVersionIdPart()); diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index e64b6d8ed27..4decd751270 100644 --- a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -54,7 +54,7 @@ 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("myResourceProvidersDstu2", List.class); + ResourceProviderFactory beans = myAppCtx.getBean("myResourceProvidersDstu2", List.class); setResourceProviders(beans); /* diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java index e2b8ad6ace9..7a693297944 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java @@ -725,7 +725,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid @Override public String getVersion() { - throw new UnsupportedOperationException(); + return FhirVersionEnum.DSTU2.getFhirVersionString(); } @Override diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 9fc085b092d..519b19d6348 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2162,7 +2162,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat long t = System.nanoTime(); StructureDefinition sd = context.fetchResource(StructureDefinition.class, url); sdTime = sdTime + (System.nanoTime() - t); - if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals(type)) && sd.hasSnapshot()) + if (sd != null && (sd.getType().equals(type) || sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/" + type)) && sd.hasSnapshot()) return sd; } return null; diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorDstu3Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorDstu3Test.java index 5edff06d8b5..58273cc5374 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorDstu3Test.java @@ -65,9 +65,7 @@ public class RequestValidatingInterceptorDstu3Test { @Before public void before() { ourLastRequestWasSearch = false; - while (ourServlet.getInterceptors().size() > 0) { - ourServlet.unregisterInterceptor(ourServlet.getInterceptors().get(0)); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); myInterceptor = new RequestValidatingInterceptor(); // myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorR4Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorR4Test.java index f2837f098fb..92839eaadd0 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorR4Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/RequestValidatingInterceptorR4Test.java @@ -63,9 +63,7 @@ public class RequestValidatingInterceptorR4Test { @Before public void before() { ourLastRequestWasSearch = false; - while (ourServlet.getInterceptors().size() > 0) { - ourServlet.unregisterInterceptor(ourServlet.getInterceptors().get(0)); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); myInterceptor = new RequestValidatingInterceptor(); // myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorDstu3Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorDstu3Test.java index dc6a8dd7da1..d8ccd553b88 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorDstu3Test.java @@ -58,9 +58,7 @@ public class ResponseValidatingInterceptorDstu3Test { @Before public void before() { myReturnResource = null; - while (ourServlet.getInterceptors().size() > 0) { - ourServlet.unregisterInterceptor(ourServlet.getInterceptors().get(0)); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); myInterceptor = new ResponseValidatingInterceptor(); // myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorR4Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorR4Test.java index 4e9ef05ff53..58a83c97574 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorR4Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ResponseValidatingInterceptorR4Test.java @@ -58,9 +58,7 @@ public class ResponseValidatingInterceptorR4Test { @Before public void before() { myReturnResource = null; - while (ourServlet.getInterceptors().size() > 0) { - ourServlet.unregisterInterceptor(ourServlet.getInterceptors().get(0)); - } + ourServlet.getInterceptorService().unregisterAllInterceptors(); myInterceptor = new ResponseValidatingInterceptor(); // myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); diff --git a/hapi-fhir-validation/src/test/resources/dstu3/fmc01-cs-binderrecommended.json b/hapi-fhir-validation/src/test/resources/dstu3/fmc01-cs-binderrecommended.json index c5cb91a9b7d..b0d583ba8c9 100644 --- a/hapi-fhir-validation/src/test/resources/dstu3/fmc01-cs-binderrecommended.json +++ b/hapi-fhir-validation/src/test/resources/dstu3/fmc01-cs-binderrecommended.json @@ -1,11 +1,11 @@ { "resourceType": "CodeSystem", - "id": "FMCYesNoUnk", + "id": "FMCBinderRecommended", "meta": { "versionId": "1", "lastUpdated": "2019-04-17T12:50:19.996+00:00" }, - "url": "binder_recommended_fkc", + "url": "http://binder_recommended_fkc", "identifier": { "value": "FMCYesNoUnk" }, diff --git a/hapi-fhir-validation/src/test/resources/dstu3/fmc01-questionnaireresponse.json b/hapi-fhir-validation/src/test/resources/dstu3/fmc01-questionnaireresponse.json index 01d8b6224bc..2847367e4eb 100644 --- a/hapi-fhir-validation/src/test/resources/dstu3/fmc01-questionnaireresponse.json +++ b/hapi-fhir-validation/src/test/resources/dstu3/fmc01-questionnaireresponse.json @@ -47,7 +47,7 @@ "answer": [ { "valueCoding": { - "system": "binder_recommended_fkc", + "system": "http://binder_recommended_fkc", "code": "AURYXIA", "display": "Auryxia" } diff --git a/hapi-fhir-validation/src/test/resources/dstu3/fmc01-vs-binderrecommended.json b/hapi-fhir-validation/src/test/resources/dstu3/fmc01-vs-binderrecommended.json index c8b483842cf..418fd8b8cbe 100644 --- a/hapi-fhir-validation/src/test/resources/dstu3/fmc01-vs-binderrecommended.json +++ b/hapi-fhir-validation/src/test/resources/dstu3/fmc01-vs-binderrecommended.json @@ -19,7 +19,7 @@ "compose": { "include": [ { - "system": "binder_recommended_fkc", + "system": "http://binder_recommended_fkc", "concept": [ { "code": "VELPHORO", diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm index cc5d7a443d9..04733b01df9 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm @@ -22,21 +22,21 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.jpa.dao.*; +import ca.uhn.fhir.jpa.util.ResourceProviderFactory; @Configuration public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jpa.config${package_suffix}.Base${versionCapitalized}Config { @Bean(name="myResourceProviders${versionCapitalized}") - public List resourceProviders${versionCapitalized}() { - List retVal = new ArrayList(); + public ResourceProviderFactory resourceProviders${versionCapitalized}() { + ResourceProviderFactory retVal = new ResourceProviderFactory(); #foreach ( $res in $resources ) - if (isSupported("${res.name}")) { - retVal.add(rp${res.declaringClassNameComplete}${versionCapitalized}()); - } + retVal.addSupplier(() -> isSupported("${res.name}") ? rp${res.declaringClassNameComplete}${versionCapitalized}() : null); #end return retVal; } + @Bean(name="myResourceDaos${versionCapitalized}") public List> resourceDaos${versionCapitalized}() { List> retVal = new ArrayList>(); diff --git a/pom.xml b/pom.xml index 3ce0cbcebb3..f468c8fe811 100755 --- a/pom.xml +++ b/pom.xml @@ -535,16 +535,16 @@ 2.6 3.8.1 10.14.2.0 - 2.0.18 - 2.3.2 - 25.0-jre + 2.3.3 + 2.3.3 + 27.1-jre 2.8.5 2.2.11_1 2.3.1 2.3.0.1 2.3.1 2.25.1 - 9.4.14.v20181114 + 9.4.17.v20190418 3.0.2 5.4.2.Final @@ -565,7 +565,7 @@ 1.2_5 1.7.25 5.1.6.RELEASE - 2.1.5.RELEASE + 2.1.6.RELEASE 2.1.1.RELEASE 1.2.2.RELEASE @@ -872,7 +872,7 @@ net.ttddyy datasource-proxy - 1.5 + 1.5.1 org.antlr @@ -1187,7 +1187,7 @@ org.mockito mockito-core - 2.25.1 + 2.27.0 org.postgresql @@ -1530,7 +1530,7 @@ org.codehaus.mojo license-maven-plugin - 1.19 + 1.20 true false @@ -1562,7 +1562,7 @@ org.jacoco jacoco-maven-plugin - 0.8.2 + 0.8.3 org.apache.maven.plugins @@ -2276,8 +2276,8 @@ hapi-fhir-cli hapi-fhir-dist examples - osgi/hapi-fhir-karaf-features - osgi/hapi-fhir-karaf-integration-tests + example-projects/hapi-fhir-base-example-embedded-ws example-projects/hapi-fhir-standalone-overlay-example example-projects/hapi-fhir-jpaserver-cds-example diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 5b9e7f6898c..446454c1afd 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -12,12 +12,14 @@ latest versions (dependent HAPI modules listed in brackets): +
  • Guava (base): 25-jre -> 27.1-jre
  • Hibernate (JPA): 5.4.1 -> 5.4.2
  • Jackson (JPA): 2.9.7 -> 2.9.8
  • Spring (JPA): 5.1.3.RELEASE -> 5.1.6.RELEASE
  • -
  • Spring-Data (JPA): 2.1.3.RELEASE -> 2.1.5.RELEASE
  • +
  • Spring-Data (JPA): 2.1.3.RELEASE -> 2.1.6.RELEASE
  • Caffeine (JPA): 2.6.2 -> 2.7.0
  • JANSI (CLI): 1.16 -> 1.17.1
  • +
  • Jetty (CLI): 9.4.14.v20181114 -> 9.4.17.v20190418
  • ]]> @@ -32,7 +34,7 @@ The hapi-fhir-jpaserver-starter project has been updated to use a properties - file for configuration, making it much easier to get started with this + file for configuration, making it much easier to get started with this project. Thanks to Sean McIlvenna for the pull request! @@ -128,7 +130,7 @@ Two expunge bug fixes: - The first bug is that the expunge operation wasn't bailing once it hit its limit. This resulted in a + The first bug is that the expunge operation wasn't bailing once it hit its limit. This resulted in a "Page size must not be less than one!" error. The second bug is that one case wasn't properly handled: when a resourceId with no version is provided. This executed the case where only resource type is provided. @@ -138,6 +140,11 @@ the resource version is not updated and no new version is created. In this situation, the update time was modified however. It will no longer be updated. + + When running the JPA server in Resource Client ID strategy mode of "ANY", using the + _id]]> search parameter could return incorrect results. This + has been corrected. + Performing a PUT or POST against a HAPI FHIR Server with no request body caused an HTTP 500 to be returned instead of a more appropriate HTTP 400. This has been @@ -171,6 +178,9 @@ ]]> project, as it is much more robust and fault tolerant. + + Ensure that database cursors are closed immediately after performing a FHIR search. + @@ -209,11 +219,11 @@ by two new interceptors: SubscriptionActivatingInterceptor that is responsible for activating subscriptions and SubscriptionMatchingInterceptor that is responsible for matching incoming resources against activated subscriptions. Call DaoConfig.addSupportedSubscriptionType(type) to configure which subscription types - are supported in your environment. If you are processing subscriptions on a separate server and only want + are supported in your environment. If you are processing subscriptions on a separate server and only want to activate subscriptions on this server, you should set DaoConfig.setSubscriptionMatchingEnabled to false. The helper method SubscriptionInterceptorLoader.registerInterceptors() will check if any subscription types are supported, and if so then load active subscriptions into the - SubscriptionRegistry and register the subscription activating interceptor. This method also registers + SubscriptionRegistry and register the subscription activating interceptor. This method also registers the subscription matching interceptor (that matches incoming resources and sends matches to subscription channels) only if DaoConfig.isSubscriptionMatchingEnabled is true. See https://github.com/jamesagnew/hapi-fhir/wiki/Proposed-Subscription-Design-Change for more @@ -420,7 +430,7 @@ AuthorizationInterceptor now rejects transactions with an invalid or unset request - using an HTTP 422 response Bundle type instead of silently refusing to authorize them. + using an HTTP 422 response Bundle type instead of silently refusing to authorize them. AuthorizationInterceptor is now able to authorize DELETE operations performed via a @@ -438,7 +448,7 @@ In a DSTU2 server, if search parameters are expressed with chains directly in the - parameter name (e.g. + parameter name (e.g. @RequiredParam(name="subject.name.family")]]>) the second part of the chain was lost when the chain was described in the server CapabilityStatement. This has been corrected. @@ -496,7 +506,7 @@ Add a "subscription-matching-strategy" meta tag to incoming subscriptions with value of IN_MEMORY or DATABASE indicating whether the subscription can be matched against new resources in-memory or - whether a call out to the database may be required. I say "may" because subscription matches fail fast + whether a call out to the database may be required. I say "may" because subscription matches fail fast so a negative match may be performed in-memory, but a positive match will require a database call. diff --git a/src/site/xdoc/doc_interceptors.xml b/src/site/xdoc/doc_interceptors.xml new file mode 100644 index 00000000000..889ed446d44 --- /dev/null +++ b/src/site/xdoc/doc_interceptors.xml @@ -0,0 +1,34 @@ + + + + + Interceptors + James Agnew + + + + +
    + +

    + HAPI FHIR 3.8.0 introduced a new interceptor framework that is used across the entire + library. In previous versions of HAPI FHIR, a "Server Interceptor" framework existed and a + separate "Client Interceptor" framework existed. These have now been combined into a single + unified (and very powerful) framework. +

    + +

    + Interceptor classes may "hook into" various points in the processing chain in both + the client and the erver. +

    + + + + + + +
    + + + +
    diff --git a/src/site/xdoc/doc_rest_server_security.xml b/src/site/xdoc/doc_rest_server_security.xml index 1204e8e96f8..cfb9339bb20 100644 --- a/src/site/xdoc/doc_rest_server_security.xml +++ b/src/site/xdoc/doc_rest_server_security.xml @@ -86,7 +86,7 @@

    HAPI FHIR 1.5 introduced a new interceptor, the - AuthorizationInterceptor. + AuthorizationInterceptor.

    This interceptor can help with the complicated task of determining whether a user