thePersistedResourceModifiedMessage
+ * in the event where submission would fail.
+ *
+ * @param thePersistedResourceModifiedMessage the primary key pointing to the persisted version (IPersistedResourceModifiedMessage) of a ResourceModifiedMessage needing submission
+ * @return true upon successful submission, false otherwise.
+ */
+ protected TransactionCallback doProcessResourceModifiedInTransaction(
+ IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) {
+ return theStatus -> {
+ boolean processed = true;
+ ResourceModifiedMessage resourceModifiedMessage = null;
+ try {
+
+ // delete the entry to lock the row to ensure unique processing
+ boolean wasDeleted = deletePersistedResourceModifiedMessage(
+ thePersistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk());
+
+ Optional optionalResourceModifiedMessage =
+ inflatePersistedResourceMessage(thePersistedResourceModifiedMessage);
+
+ if (wasDeleted && optionalResourceModifiedMessage.isPresent()) {
+ // the PK did exist and we were able to deleted it, ie, we are the only one processing the message
+ resourceModifiedMessage = optionalResourceModifiedMessage.get();
+ submitResourceModified(resourceModifiedMessage);
+ }
+
+ } catch (MessageDeliveryException exception) {
+ // we encountered an issue when trying to send the message so mark the transaction for rollback
+ ourLog.error(
+ "Channel submission failed for resource with id {} matching subscription with id {}. Further attempts will be performed at later time.",
+ resourceModifiedMessage.getPayloadId(),
+ resourceModifiedMessage.getSubscriptionId());
+ processed = false;
+ theStatus.setRollbackOnly();
+ }
+
+ return processed;
+ };
+ }
+
+ private Optional inflatePersistedResourceMessage(
+ IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) {
+ ResourceModifiedMessage resourceModifiedMessage = null;
+
+ try {
+
+ resourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(
+ thePersistedResourceModifiedMessage);
+
+ } catch (ResourceNotFoundException e) {
+ IPersistedResourceModifiedMessagePK persistedResourceModifiedMessagePk =
+ thePersistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk();
+
+ IdType idType = new IdType(
+ thePersistedResourceModifiedMessage.getResourceType(),
+ persistedResourceModifiedMessagePk.getResourcePid(),
+ persistedResourceModifiedMessagePk.getResourceVersion());
+
+ ourLog.warn(
+ "Scheduled submission will be ignored since resource {} cannot be found", idType.asStringValue());
+ }
+
+ return Optional.ofNullable(resourceModifiedMessage);
+ }
+
+ private boolean deletePersistedResourceModifiedMessage(IPersistedResourceModifiedMessagePK theResourceModifiedPK) {
+
+ try {
+ // delete the entry to lock the row to ensure unique processing
+ return myResourceModifiedMessagePersistenceSvc.deleteByPK(theResourceModifiedPK);
+ } catch (ResourceNotFoundException exception) {
+ ourLog.warn(
+ "thePersistedResourceModifiedMessage with {} and version {} could not be deleted as it may have already been deleted.",
+ theResourceModifiedPK.getResourcePid(),
+ theResourceModifiedPK.getResourceVersion());
+ // we were not able to delete the pk. this implies that someone else did read/delete the PK and processed
+ // the message
+ // successfully before we did.
+
+ return false;
+ }
+ }
+
+ private ChannelProducerSettings getChannelProducerSettings() {
+ ChannelProducerSettings channelProducerSettings = new ChannelProducerSettings();
+ channelProducerSettings.setQualifyChannelName(myStorageSettings.isQualifySubscriptionMatchingChannelName());
+ return channelProducerSettings;
+ }
+
+ public IChannelProducer getProcessingChannelForUnitTest() {
+ startIfNeeded();
+ return (IChannelProducer) myMatchingChannel;
+ }
+}
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/util/SubscriptionDebugLogInterceptor.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/util/SubscriptionDebugLogInterceptor.java
index ebbfd974d38..6494f59a2e1 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/util/SubscriptionDebugLogInterceptor.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/util/SubscriptionDebugLogInterceptor.java
@@ -39,7 +39,7 @@ import java.util.function.Function;
* This interceptor can be used for troubleshooting subscription processing. It provides very
* detailed logging about the subscription processing pipeline.
*
- * This interceptor loges each step in the processing pipeline with a
+ * This interceptor logs each step in the processing pipeline with a
* different event code, using the event codes itemized in
* {@link EventCodeEnum}. By default these are each placed in a logger with
* a different name (e.g. ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor.SUBS20
@@ -91,7 +91,7 @@ public class SubscriptionDebugLogInterceptor {
}
log(
EventCodeEnum.SUBS1,
- "Resource {} was submitted to the processing pipeline (op={})",
+ "Resource {} is starting the processing pipeline (op={})",
resourceId,
theMessage.getOperationType());
}
diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionMatcherInterceptorTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionMatcherInterceptorTest.java
deleted file mode 100644
index 889e2963160..00000000000
--- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionMatcherInterceptorTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package ca.uhn.fhir.jpa.subscription.submit.interceptor;
-
-import ca.uhn.fhir.jpa.model.entity.StorageSettings;
-import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
-import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.Set;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-
-@ExtendWith(MockitoExtension.class)
-public class SubscriptionMatcherInterceptorTest {
-
- @Mock
- StorageSettings myStorageSettings;
- @Mock
- SubscriptionChannelFactory mySubscriptionChannelFactory;
- @InjectMocks
- SubscriptionMatcherInterceptor myUnitUnderTest;
- @Captor
- ArgumentCaptor myArgumentCaptor;
-
- @ParameterizedTest
- @ValueSource(booleans = {false, true})
- public void testMethodStartIfNeeded_withQualifySubscriptionMatchingChannelNameProperty_mayQualifyChannelName(boolean theIsQualifySubMatchingChannelName){
- // given
- boolean expectedResult = theIsQualifySubMatchingChannelName;
- when(myStorageSettings.isQualifySubscriptionMatchingChannelName()).thenReturn(theIsQualifySubMatchingChannelName);
- when(myStorageSettings.getSupportedSubscriptionTypes()).thenReturn(Set.of(RESTHOOK));
-
- // when
- myUnitUnderTest.startIfNeeded();
-
- // then
- ChannelProducerSettings capturedChannelProducerSettings = getCapturedChannelProducerSettings();
- assertThat(capturedChannelProducerSettings.isQualifyChannelName(), is(expectedResult));
-
- }
-
- private ChannelProducerSettings getCapturedChannelProducerSettings(){
- verify(mySubscriptionChannelFactory).newMatchingSendingChannel(anyString(), myArgumentCaptor.capture());
- return myArgumentCaptor.getValue();
- }
-
-
-}
diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java
index 6ad42669f81..f5f11cbfb34 100644
--- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java
+++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java
@@ -6,12 +6,14 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.cache.IResourceVersionSvc;
+import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.searchparam.config.SearchParamConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig;
+import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
import org.hl7.fhir.dstu2.model.Subscription;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -21,6 +23,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.transaction.PlatformTransactionManager;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
@@ -34,24 +37,12 @@ import static org.mockito.Mockito.verify;
})
public class SubscriptionSubmitInterceptorLoaderTest {
- @MockBean
- private ISearchParamProvider mySearchParamProvider;
- @MockBean
- private IInterceptorService myInterceptorService;
- @MockBean
- private IValidationSupport myValidationSupport;
- @MockBean
- private SubscriptionChannelFactory mySubscriptionChannelFactory;
- @MockBean
- private DaoRegistry myDaoRegistry;
@Autowired
private SubscriptionSubmitInterceptorLoader mySubscriptionSubmitInterceptorLoader;
@Autowired
private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
@MockBean
- private IResourceVersionSvc myResourceVersionSvc;
- @MockBean
- private IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
+ private IInterceptorService myInterceptorService;
/**
* It should be possible to run only the {@link SubscriptionSubmitterConfig} without the
@@ -82,6 +73,25 @@ public class SubscriptionSubmitInterceptorLoaderTest {
return storageSettings;
}
+ @MockBean
+ private ISearchParamProvider mySearchParamProvider;
+ @MockBean
+ private IValidationSupport myValidationSupport;
+ @MockBean
+ private SubscriptionChannelFactory mySubscriptionChannelFactory;
+ @MockBean
+ private DaoRegistry myDaoRegistry;
+ @MockBean
+ private IResourceVersionSvc myResourceVersionSvc;
+ @MockBean
+ private IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
+ @MockBean
+ private PlatformTransactionManager myPlatformTransactionManager;
+ @MockBean
+ private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc;
+ @MockBean
+ private IHapiTransactionService myHapiTransactionService;
+
}
diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTriggerMatcherTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTriggerMatcherTest.java
index ae47a026bb7..e06c969510d 100644
--- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTriggerMatcherTest.java
+++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTriggerMatcherTest.java
@@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.r5.model.Encounter;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.SubscriptionTopic;
diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml
index beab9e4ab2c..d6e6a1f94c3 100644
--- a/hapi-fhir-jpaserver-test-dstu2/pom.xml
+++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/BaseSearchSvc.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/BaseSearchSvc.java
index 37f52a21cf1..46f8fd95af4 100644
--- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/BaseSearchSvc.java
+++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/BaseSearchSvc.java
@@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
+import ca.uhn.fhir.jpa.svc.MockHapiTransactionService;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java
index d9b42cb7683..6f993478058 100644
--- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java
+++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java
@@ -210,7 +210,6 @@ public class RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test ext
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
-
runInTransaction(() -> {
ourLog.info("All token indexes:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml
index 01e61f2e7dc..d812bae0c9c 100644
--- a/hapi-fhir-jpaserver-test-dstu3/pom.xml
+++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml
index 1626f6c4d6e..fa1a1f2432c 100644
--- a/hapi-fhir-jpaserver-test-r4/pom.xml
+++ b/hapi-fhir-jpaserver-test-r4/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java
index bdfae2d54fd..98fea30536f 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java
@@ -16,8 +16,8 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
-import ca.uhn.fhir.jpa.search.MockHapiTransactionService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.jpa.svc.MockHapiTransactionService;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
index 13511df2607..826a7b382c3 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java
@@ -25,7 +25,7 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
-import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc;
import ca.uhn.fhir.jpa.term.TermReadSvcImpl;
import ca.uhn.fhir.jpa.util.SqlQuery;
@@ -126,6 +126,7 @@ import static org.mockito.Mockito.when;
@SuppressWarnings("JavadocBlankLines")
@TestMethodOrder(MethodOrderer.MethodName.class)
public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test {
+
@RegisterExtension
@Order(0)
public static final RestfulServerExtension ourServer = new RestfulServerExtension(FhirContext.forR4Cached())
@@ -139,7 +140,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
@Autowired
private ISubscriptionTriggeringSvc mySubscriptionTriggeringSvc;
@Autowired
- private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
+ private ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;;
@Autowired
private ReindexStep myReindexStep;
@Autowired
@@ -3090,7 +3091,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
// Setup
myStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK);
- mySubscriptionMatcherInterceptor.startIfNeeded();
+ myResourceModifiedSubmitterSvc.startIfNeeded();
for (int i = 0; i < 10; i++) {
createPatient(withActiveTrue());
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java
index 4cb7788307e..1b315cdca30 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java
@@ -2,12 +2,14 @@ package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
+import ca.uhn.fhir.jpa.dao.data.IResourceModifiedDao;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
-import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.test.util.SubscriptionTestUtil;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
+import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderExtension;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import ca.uhn.fhir.test.utilities.server.TransactionCapturingProviderExtension;
@@ -61,7 +63,11 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
@Autowired
protected SubscriptionTestUtil mySubscriptionTestUtil;
@Autowired
- protected SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
+ protected ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
+ @Autowired
+ protected IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc;
+ @Autowired
+ protected IResourceModifiedDao myResourceModifiedDao;
protected CountingInterceptor myCountingInterceptor;
protected List mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
@Autowired
@@ -84,6 +90,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
myStorageSettings.setAllowMultipleDelete(new JpaStorageSettings().isAllowMultipleDelete());
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
+ myResourceModifiedDao.deleteAll();
}
@BeforeEach
@@ -102,7 +109,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
waitForActivatedSubscriptionCount(0);
}
- LinkedBlockingChannel processingChannel = mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
+ LinkedBlockingChannel processingChannel = (LinkedBlockingChannel) myResourceModifiedSubmitterSvc.getProcessingChannelForUnitTest();
if (processingChannel != null) {
processingChannel.clearInterceptorsForUnitTest();
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/async/AsyncSubscriptionMessageSubmissionIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/async/AsyncSubscriptionMessageSubmissionIT.java
new file mode 100644
index 00000000000..13a0b4b686c
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/async/AsyncSubscriptionMessageSubmissionIT.java
@@ -0,0 +1,158 @@
+package ca.uhn.fhir.jpa.subscription.async;
+
+import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
+import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test;
+import ca.uhn.fhir.jpa.subscription.submit.interceptor.SynchronousSubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.subscription.channel.api.ChannelConsumerSettings;
+import ca.uhn.fhir.jpa.subscription.channel.api.IChannelReceiver;
+import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
+import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
+import ca.uhn.fhir.jpa.subscription.message.TestQueueConsumerHandler;
+import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
+import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
+import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.test.util.StoppableSubscriptionDeliveringRestHookSubscriber;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.Coding;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Subscription;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+@ContextConfiguration(classes = {AsyncSubscriptionMessageSubmissionIT.SpringConfig.class})
+public class AsyncSubscriptionMessageSubmissionIT extends BaseSubscriptionsR4Test {
+
+ @SpyBean
+ IResourceModifiedConsumer myResourceModifiedConsumer;
+
+ @Autowired
+ AsyncResourceModifiedSubmitterSvc myAsyncResourceModifiedSubmitterSvc;
+
+ @Autowired
+ private SubscriptionChannelFactory myChannelFactory;
+
+ @Autowired SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
+
+ @Autowired
+ StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber;
+ private TestQueueConsumerHandler myQueueConsumerHandler;
+
+ @AfterEach
+ public void cleanupStoppableSubscriptionDeliveringRestHookSubscriber() {
+ myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(null);
+ myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
+ myStorageSettings.setTriggerSubscriptionsForNonVersioningChanges(new JpaStorageSettings().isTriggerSubscriptionsForNonVersioningChanges());
+ myStorageSettings.setTagStorageMode(new JpaStorageSettings().getTagStorageMode());
+ }
+
+ @BeforeEach
+ public void beforeRegisterRestHookListenerAndSchedulePoisonPillInterceptor() {
+ mySubscriptionTestUtil.registerMessageInterceptor();
+
+ IChannelReceiver receiver = myChannelFactory.newMatchingReceivingChannel("my-queue-name", new ChannelConsumerSettings());
+ myQueueConsumerHandler = new TestQueueConsumerHandler();
+ receiver.subscribe(myQueueConsumerHandler);
+
+ myStorageSettings.setTagStorageMode(JpaStorageSettings.TagStorageModeEnum.NON_VERSIONED);
+ }
+
+ @Test
+ public void testSpringInjects_BeanOfTypeSubscriptionMatchingInterceptor_whenBeanDeclarationIsOverwrittenLocally(){
+ assertFalse(mySubscriptionMatcherInterceptor instanceof SynchronousSubscriptionMatcherInterceptor);
+ }
+
+ @Test
+ // the purpose of this test is to assert that a resource matching a given subscription is
+ // delivered asynchronously to the subscription processing pipeline.
+ public void testAsynchronousDeliveryOfResourceMatchingASubscription_willSucceed() throws Exception {
+ String aCode = "zoop";
+ String aSystem = "SNOMED-CT";
+ // given
+ createAndSubmitSubscriptionWithCriteria("[Observation]");
+ waitForActivatedSubscriptionCount(1);
+
+ // when
+ Observation obs = sendObservation(aCode, aSystem);
+
+ assertCountOfResourcesNeedingSubmission(2); // the subscription and the observation
+ assertCountOfResourcesReceivedAtSubscriptionTerminalEndpoint(0);
+
+ // since scheduled tasks are disabled during tests, let's trigger a submission
+ // just like the AsyncResourceModifiedProcessingSchedulerSvc would.
+ myAsyncResourceModifiedSubmitterSvc.runDeliveryPass();
+
+ //then
+ waitForQueueToDrain();
+ assertCountOfResourcesNeedingSubmission(0);
+ assertCountOfResourcesReceivedAtSubscriptionTerminalEndpoint(1);
+
+ Observation observation = (Observation) fetchSingleResourceFromSubscriptionTerminalEndpoint();
+ Coding coding = observation.getCode().getCodingFirstRep();
+
+ assertThat(coding.getCode(), equalTo(aCode));
+ assertThat(coding.getSystem(), equalTo(aSystem));
+
+ }
+
+ private void assertCountOfResourcesNeedingSubmission(int theExpectedCount) {
+ assertThat(myResourceModifiedMessagePersistenceSvc.findAllOrderedByCreatedTime(), hasSize(theExpectedCount));
+ }
+
+ private Subscription createAndSubmitSubscriptionWithCriteria(String theCriteria) {
+ Subscription subscription = new Subscription();
+ subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
+ subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
+ subscription.setCriteria(theCriteria);
+
+ Subscription.SubscriptionChannelComponent channel = subscription.getChannel();
+ channel.setType(Subscription.SubscriptionChannelType.MESSAGE);
+ channel.setPayload("application/fhir+json");
+ channel.setEndpoint("channel:my-queue-name");
+
+ subscription.setChannel(channel);
+ postOrPutSubscription(subscription);
+
+ myAsyncResourceModifiedSubmitterSvc.runDeliveryPass();
+
+ return subscription;
+ }
+
+
+ private IBaseResource fetchSingleResourceFromSubscriptionTerminalEndpoint() {
+ assertThat(myQueueConsumerHandler.getMessages().size(), is(equalTo(1)));
+ ResourceModifiedJsonMessage resourceModifiedJsonMessage = myQueueConsumerHandler.getMessages().get(0);
+ ResourceModifiedMessage payload = resourceModifiedJsonMessage.getPayload();
+ String payloadString = payload.getPayloadString();
+ IBaseResource resource = myFhirContext.newJsonParser().parseResource(payloadString);
+ myQueueConsumerHandler.clearMessages();
+ return resource;
+ }
+
+ private void assertCountOfResourcesReceivedAtSubscriptionTerminalEndpoint(int expectedCount) {
+ assertThat(myQueueConsumerHandler.getMessages(), hasSize(expectedCount));
+ }
+
+ @Configuration
+ public static class SpringConfig {
+
+ @Primary
+ @Bean
+ public SubscriptionMatcherInterceptor subscriptionMatcherInterceptor() {
+ return new SubscriptionMatcherInterceptor();
+ }
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java
index 2e9b8f84edb..540f5ae1f7d 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java
@@ -1,6 +1,11 @@
package ca.uhn.fhir.jpa.subscription.message;
+import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
+import ca.uhn.fhir.jpa.dao.data.IResourceModifiedDao;
+import ca.uhn.fhir.jpa.model.entity.IPersistedResourceModifiedMessage;
+import ca.uhn.fhir.jpa.model.entity.IPersistedResourceModifiedMessagePK;
+import ca.uhn.fhir.jpa.model.entity.PersistedResourceModifiedMessageEntityPK;
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test;
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelConsumerSettings;
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelReceiver;
@@ -11,20 +16,28 @@ import ca.uhn.fhir.jpa.test.util.StoppableSubscriptionDeliveringRestHookSubscrib
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.AdditionalRequestHeadersInterceptor;
+import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Subscription;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.support.TransactionTemplate;
import java.util.List;
import java.util.stream.Collectors;
@@ -49,6 +62,12 @@ public class MessageSubscriptionR4Test extends BaseSubscriptionsR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(MessageSubscriptionR4Test.class);
private TestQueueConsumerHandler handler;
+ @Autowired
+ IResourceModifiedDao myResourceModifiedDao;
+
+ @Autowired
+ private PlatformTransactionManager myTxManager;
+
@Autowired
StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber;
@@ -176,6 +195,109 @@ public class MessageSubscriptionR4Test extends BaseSubscriptionsR4Test {
}
+ @Test
+ public void testMethodFindAllOrdered_willReturnAllPersistedResourceModifiedMessagesOrderedByCreatedTime(){
+ mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
+
+ // given
+ Patient patient = sendPatient();
+ Organization organization = sendOrganization();
+
+ ResourceModifiedMessage patientResourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, patient, BaseResourceMessage.OperationTypeEnum.CREATE);
+ ResourceModifiedMessage organizationResourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, organization, BaseResourceMessage.OperationTypeEnum.CREATE);
+
+ IPersistedResourceModifiedMessage patientPersistedMessage = myResourceModifiedMessagePersistenceSvc.persist(patientResourceModifiedMessage);
+ IPersistedResourceModifiedMessage organizationPersistedMessage = myResourceModifiedMessagePersistenceSvc.persist(organizationResourceModifiedMessage);
+
+ // when
+ List allPersisted = myResourceModifiedMessagePersistenceSvc.findAllOrderedByCreatedTime();
+
+ // then
+ assertOnPksAndOrder(allPersisted, List.of(patientPersistedMessage, organizationPersistedMessage));
+
+ }
+
+ @Test
+ public void testMethodDeleteByPK_whenEntityExists_willDeleteTheEntityAndReturnTrue(){
+ mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
+
+ // given
+ TransactionTemplate transactionTemplate = new TransactionTemplate(myTxManager);
+ Patient patient = sendPatient();
+
+ ResourceModifiedMessage patientResourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, patient, BaseResourceMessage.OperationTypeEnum.CREATE);
+ IPersistedResourceModifiedMessage persistedResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.persist(patientResourceModifiedMessage);
+
+ // when
+ boolean wasDeleted = transactionTemplate.execute(tx -> myResourceModifiedMessagePersistenceSvc.deleteByPK(persistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk()));
+
+ // then
+ assertThat(wasDeleted, is(Boolean.TRUE));
+ assertThat(myResourceModifiedMessagePersistenceSvc.findAllOrderedByCreatedTime(), hasSize(0));
+ }
+
+ @Test
+ public void testMethodDeleteByPK_whenEntityDoesNotExist_willReturnFalse(){
+ mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
+
+ // given
+ TransactionTemplate transactionTemplate = new TransactionTemplate(myTxManager);
+ IPersistedResourceModifiedMessagePK nonExistentResourceWithPk = PersistedResourceModifiedMessageEntityPK.with("one", "one");
+
+ // when
+ boolean wasDeleted = transactionTemplate.execute(tx -> myResourceModifiedMessagePersistenceSvc.deleteByPK(nonExistentResourceWithPk));
+
+ // then
+ assertThat(wasDeleted, is(Boolean.FALSE));
+ }
+
+ @Test
+ public void testPersistedResourceModifiedMessage_whenFetchFromDb_willEqualOriginalMessage() throws JsonProcessingException {
+ mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
+ // given
+ TransactionTemplate transactionTemplate = new TransactionTemplate(myTxManager);
+ Observation obs = sendObservation("zoop", "SNOMED-CT", "theExplicitSource", "theRequestId");
+
+ ResourceModifiedMessage originalResourceModifiedMessage = createResourceModifiedMessage(obs);
+
+ transactionTemplate.execute(tx -> {
+
+ IPersistedResourceModifiedMessage persistedResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.persist(originalResourceModifiedMessage);
+
+ // when
+ ResourceModifiedMessage restoredResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(persistedResourceModifiedMessage);
+
+ // then
+ assertEquals(toJson(originalResourceModifiedMessage), toJson(restoredResourceModifiedMessage));
+ assertEquals(originalResourceModifiedMessage, restoredResourceModifiedMessage);
+
+ return null;
+ });
+
+ }
+
+ private ResourceModifiedMessage createResourceModifiedMessage(Observation theObservation){
+ ResourceModifiedMessage retVal = new ResourceModifiedMessage(myFhirContext, theObservation, BaseResourceMessage.OperationTypeEnum.CREATE);
+ retVal.setSubscriptionId("subId");
+ retVal.setTransactionId("txId");
+ retVal.setMessageKey("messageKey");
+ retVal.setMediaType("json");
+ retVal.setAttribute("attKey", "attValue");
+ retVal.setPartitionId(RequestPartitionId.allPartitions());
+ return retVal;
+ }
+
+ private static void assertEquals(ResourceModifiedMessage theMsg, ResourceModifiedMessage theComparedTo){
+ assertThat(theMsg.getPayloadId(), equalTo(theComparedTo.getPayloadId()));
+ assertThat(theMsg.getOperationType(), equalTo(theComparedTo.getOperationType()));
+ assertThat(theMsg.getPayloadString(), equalTo(theComparedTo.getPayloadString()));
+ assertThat(theMsg.getSubscriptionId(), equalTo(theComparedTo.getSubscriptionId()));
+ assertThat(theMsg.getMediaType(), equalTo(theComparedTo.getMediaType()));
+ assertThat(theMsg.getMessageKeyOrNull(), equalTo(theComparedTo.getMessageKeyOrNull()));
+ assertThat(theMsg.getTransactionId(), equalTo(theComparedTo.getTransactionId()));
+ assertThat(theMsg.getAttributes(), equalTo(theComparedTo.getAttributes()));
+ }
+
private void maybeAddHeaderInterceptor(IGenericClient theClient, List theHeaders) {
if(theHeaders.isEmpty()){
return;
@@ -215,4 +337,32 @@ public class MessageSubscriptionR4Test extends BaseSubscriptionsR4Test {
return (T) resource;
}
+ private static void assertEquals(String theMsg, String theComparedTo){
+ assertThat(theMsg, equalTo(theComparedTo));
+ }
+
+ private static String toJson(Object theRequest) {
+ try {
+ return new ObjectMapper().writer().writeValueAsString(theRequest);
+ } catch (JsonProcessingException theE) {
+ throw new AssertionError("Failure during serialization: " + theE);
+ }
+ }
+
+ private static void assertOnPksAndOrder(List theFetchedResourceModifiedMessageList, List theCompareToList ){
+ assertThat(theFetchedResourceModifiedMessageList, hasSize(theCompareToList.size()));
+
+ List fetchedPks = theFetchedResourceModifiedMessageList
+ .stream()
+ .map(IPersistedResourceModifiedMessage::getPersistedResourceModifiedMessagePk)
+ .collect(Collectors.toList());
+
+ List compareToPks = theCompareToList
+ .stream()
+ .map(IPersistedResourceModifiedMessage::getPersistedResourceModifiedMessagePk)
+ .collect(Collectors.toList());
+
+ Assertions.assertEquals(fetchedPks, compareToPks);
+
+ }
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java
index 30e0e1ef63b..b4f22254adb 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java
@@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.subscription.resthook;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
-import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.test.util.SubscriptionTestUtil;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
@@ -52,7 +52,7 @@ public class RestHookActivatesPreExistingSubscriptionsR4Test extends BaseResourc
@Autowired
private SubscriptionTestUtil mySubscriptionTestUtil;
@Autowired
- private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
+ private ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
@AfterEach
public void afterUnregisterRestHookListener() {
@@ -63,7 +63,7 @@ public class RestHookActivatesPreExistingSubscriptionsR4Test extends BaseResourc
@BeforeEach
public void beforeSetSubscriptionActivatingInterceptor() {
myStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK);
- mySubscriptionMatcherInterceptor.startIfNeeded();
+ myResourceModifiedSubmitterSvc.startIfNeeded();
mySubscriptionLoader.doSyncSubscriptionsForUnitTest();
}
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java
index 68152e7f1a2..ace7684317f 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java
@@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.subscription.resthook;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.test.util.StoppableSubscriptionDeliveringRestHookSubscriber;
import ca.uhn.fhir.jpa.topic.SubscriptionTopicDispatcher;
import ca.uhn.fhir.jpa.topic.SubscriptionTopicRegistry;
@@ -31,6 +32,7 @@ import org.hl7.fhir.r4.model.SearchParameter;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Subscription;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -64,6 +66,9 @@ import static org.junit.jupiter.api.Assertions.fail;
public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(RestHookTestR4Test.class);
+ @Autowired
+ ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
+
@Autowired
StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber;
@Autowired(required = false)
@@ -113,7 +118,6 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
assertEquals("IN_MEMORY", subscription.getMeta().getTag().get(0).getCode());
}
-
@Test
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
String payload = "application/fhir+json";
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java
new file mode 100644
index 00000000000..872ec955c81
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java
@@ -0,0 +1,141 @@
+package ca.uhn.fhir.jpa.subscription.svc;
+
+import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
+import ca.uhn.fhir.jpa.model.entity.ResourceModifiedEntity;
+import ca.uhn.fhir.jpa.model.entity.StorageSettings;
+import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
+import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer;
+import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
+import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
+import ca.uhn.fhir.jpa.svc.MockHapiTransactionService;
+import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.messaging.MessageDeliveryException;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.SimpleTransactionStatus;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class ResourceModifiedSubmitterSvcTest {
+
+ @Mock
+ StorageSettings myStorageSettings;
+ @Mock
+ SubscriptionChannelFactory mySubscriptionChannelFactory;
+ @Mock
+ IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc;
+ @Captor
+ ArgumentCaptor myArgumentCaptor;
+ @Mock
+ IChannelProducer myChannelProducer;
+
+ ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
+ TransactionStatus myCapturingTransactionStatus;
+
+ @BeforeEach
+ public void beforeEach(){
+ myCapturingTransactionStatus = new SimpleTransactionStatus();
+ lenient().when(myStorageSettings.hasSupportedSubscriptionTypes()).thenReturn(true);
+ lenient().when(mySubscriptionChannelFactory.newMatchingSendingChannel(anyString(), any())).thenReturn(myChannelProducer);
+
+ IHapiTransactionService hapiTransactionService = new MockHapiTransactionService(myCapturingTransactionStatus);
+ myResourceModifiedSubmitterSvc = new ResourceModifiedSubmitterSvc(
+ myStorageSettings,
+ mySubscriptionChannelFactory,
+ myResourceModifiedMessagePersistenceSvc,
+ hapiTransactionService);
+
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ public void testMethodStartIfNeeded_withQualifySubscriptionMatchingChannelNameProperty_mayQualifyChannelName(boolean theIsQualifySubMatchingChannelName){
+ // given
+ boolean expectedResult = theIsQualifySubMatchingChannelName;
+ when(myStorageSettings.isQualifySubscriptionMatchingChannelName()).thenReturn(theIsQualifySubMatchingChannelName);
+
+ // when
+ myResourceModifiedSubmitterSvc.startIfNeeded();
+
+ // then
+ ChannelProducerSettings capturedChannelProducerSettings = getCapturedChannelProducerSettings();
+ assertThat(capturedChannelProducerSettings.isQualifyChannelName(), is(expectedResult));
+
+ }
+
+ @Test
+ public void testSubmitPersisedResourceModifiedMessage_withExistingPersistedResourceModifiedMessage_willSucceed(){
+ // given
+ // a successful deletion implies that the message did exist.
+ when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true);
+ when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage());
+
+ // when
+ boolean wasProcessed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(new ResourceModifiedEntity());
+
+ // then
+ assertThat(wasProcessed, is(Boolean.TRUE));
+ assertThat(myCapturingTransactionStatus.isRollbackOnly(), is(Boolean.FALSE));
+ verify(myChannelProducer, times(1)).send(any());
+
+ }
+
+ @Test
+ public void testSubmitPersisedResourceModifiedMessage_whenMessageWasAlreadyProcess_willSucceed(){
+ // given
+ // deletion fails, someone else was faster and processed the message
+ when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(false);
+ when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage());
+
+ // when
+ boolean wasProcessed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(new ResourceModifiedEntity());
+
+ // then
+ assertThat(wasProcessed, is(Boolean.TRUE));
+ assertThat(myCapturingTransactionStatus.isRollbackOnly(), is(Boolean.FALSE));
+ // we do not send a message which was already sent
+ verify(myChannelProducer, times(0)).send(any());
+
+ }
+
+ @Test
+ public void testSubmitPersisedResourceModifiedMessage_whitErrorOnSending_willRollbackDeletion(){
+ // given
+ when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true);
+ when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage());
+
+ // simulate failure writing to the channel
+ when(myChannelProducer.send(any())).thenThrow(new MessageDeliveryException("sendingError"));
+
+ // when
+ boolean wasProcessed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(new ResourceModifiedEntity());
+
+ // then
+ assertThat(wasProcessed, is(Boolean.FALSE));
+ assertThat(myCapturingTransactionStatus.isRollbackOnly(), is(Boolean.TRUE));
+
+ }
+
+ private ChannelProducerSettings getCapturedChannelProducerSettings(){
+ verify(mySubscriptionChannelFactory).newMatchingSendingChannel(anyString(), myArgumentCaptor.capture());
+ return myArgumentCaptor.getValue();
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml
index b1bb1131bb2..2fefb661a04 100644
--- a/hapi-fhir-jpaserver-test-r4b/pom.xml
+++ b/hapi-fhir-jpaserver-test-r4b/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java
index 9dd1197fce1..bd551395c22 100644
--- a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java
+++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java
@@ -4,7 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.provider.r4b.BaseResourceProviderR4BTest;
import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
-import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.test.util.SubscriptionTestUtil;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
@@ -63,7 +63,7 @@ public abstract class BaseSubscriptionsR4BTest extends BaseResourceProviderR4BTe
@Autowired
protected SubscriptionTestUtil mySubscriptionTestUtil;
@Autowired
- protected SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
+ protected ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
protected CountingInterceptor myCountingInterceptor;
protected List mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
@Autowired
@@ -104,12 +104,12 @@ public abstract class BaseSubscriptionsR4BTest extends BaseResourceProviderR4BTe
waitForActivatedSubscriptionCount(0);
}
- LinkedBlockingChannel processingChannel = mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
+ myCountingInterceptor = new CountingInterceptor();
+
+ LinkedBlockingChannel processingChannel = (LinkedBlockingChannel) myResourceModifiedSubmitterSvc.getProcessingChannelForUnitTest();
+
if (processingChannel != null) {
processingChannel.clearInterceptorsForUnitTest();
- }
- myCountingInterceptor = new CountingInterceptor();
- if (processingChannel != null) {
processingChannel.addInterceptor(myCountingInterceptor);
}
}
diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestR4BTest.java
index 2537f195549..2e8761c363a 100644
--- a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestR4BTest.java
+++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestR4BTest.java
@@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.test.util.StoppableSubscriptionDeliveringRestHookSubscriber;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.CacheControlDirective;
@@ -25,6 +26,7 @@ import org.hl7.fhir.r4b.model.SearchParameter;
import org.hl7.fhir.r4b.model.StringType;
import org.hl7.fhir.r4b.model.Subscription;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -56,6 +58,9 @@ import static org.junit.jupiter.api.Assertions.fail;
public class RestHookTestR4BTest extends BaseSubscriptionsR4BTest {
private static final Logger ourLog = LoggerFactory.getLogger(RestHookTestR4BTest.class);
+ @Autowired
+ ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
+
@Autowired
StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber;
diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml
index 42100203521..834edcf5768 100644
--- a/hapi-fhir-jpaserver-test-r5/pom.xml
+++ b/hapi-fhir-jpaserver-test-r5/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java
index f372bee9ec1..a39f187b5ae 100644
--- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java
+++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java
@@ -12,7 +12,7 @@ import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
import ca.uhn.fhir.jpa.subscription.model.CanonicalTopicSubscriptionFilter;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
-import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.test.util.SubscriptionTestUtil;
import ca.uhn.fhir.jpa.topic.SubscriptionTopicLoader;
import ca.uhn.fhir.jpa.topic.SubscriptionTopicRegistry;
@@ -73,7 +73,7 @@ public abstract class BaseSubscriptionsR5Test extends BaseResourceProviderR5Test
@Autowired
protected SubscriptionTestUtil mySubscriptionTestUtil;
@Autowired
- protected SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
+ protected ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
protected CountingInterceptor myCountingInterceptor;
protected List mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
@Autowired
@@ -110,7 +110,7 @@ public abstract class BaseSubscriptionsR5Test extends BaseResourceProviderR5Test
waitForActivatedSubscriptionCount(0);
}
- LinkedBlockingChannel processingChannel = mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
+ LinkedBlockingChannel processingChannel = (LinkedBlockingChannel) myResourceModifiedSubmitterSvc.getProcessingChannelForUnitTest();
if (processingChannel != null) {
processingChannel.clearInterceptorsForUnitTest();
}
diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml
index ce84de56ef4..d774771251e 100644
--- a/hapi-fhir-jpaserver-test-utilities/pom.xml
+++ b/hapi-fhir-jpaserver-test-utilities/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/MockHapiTransactionService.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java
similarity index 73%
rename from hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/MockHapiTransactionService.java
rename to hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java
index 76a080e4ca1..29c2eae81ea 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/MockHapiTransactionService.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java
@@ -17,9 +17,10 @@
* limitations under the License.
* #L%
*/
-package ca.uhn.fhir.jpa.search;
+package ca.uhn.fhir.jpa.svc;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
+import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.SimpleTransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
@@ -27,9 +28,19 @@ import javax.annotation.Nullable;
public class MockHapiTransactionService extends HapiTransactionService {
+ private TransactionStatus myTransactionStatus;
+
+ public MockHapiTransactionService() {
+ this(new SimpleTransactionStatus());
+ }
+
+ public MockHapiTransactionService(TransactionStatus theTransactionStatus) {
+ myTransactionStatus = theTransactionStatus;
+ }
+
@Nullable
@Override
protected T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback theCallback) {
- return theCallback.doInTransaction(new SimpleTransactionStatus());
+ return theCallback.doInTransaction(myTransactionStatus);
}
}
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java
index 6d05e2e500d..ad9e8ebf4ce 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java
@@ -25,10 +25,16 @@ import ca.uhn.fhir.jpa.batch2.JpaBatch2Config;
import ca.uhn.fhir.jpa.config.HapiJpaConfig;
import ca.uhn.fhir.jpa.config.JpaDstu2Config;
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
+import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect;
+import ca.uhn.fhir.jpa.model.entity.StorageSettings;
+import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
+import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
+import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
import ca.uhn.fhir.system.HapiTestSystemProperties;
import ca.uhn.fhir.validation.IInstanceValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
@@ -205,4 +211,5 @@ public class TestDstu2Config {
return requestValidator;
}
+
}
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java
index a7d2881eb7e..08515a33a31 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java
@@ -26,15 +26,21 @@ import ca.uhn.fhir.jpa.config.HapiJpaConfig;
import ca.uhn.fhir.jpa.config.PackageLoaderConfig;
import ca.uhn.fhir.jpa.config.dstu3.JpaDstu3Config;
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
+import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect;
+import ca.uhn.fhir.jpa.model.entity.StorageSettings;
+import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender;
+import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.mail.IMailSvc;
import ca.uhn.fhir.rest.server.mail.MailConfig;
import ca.uhn.fhir.rest.server.mail.MailSvc;
+import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
import ca.uhn.fhir.system.HapiTestSystemProperties;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
@@ -220,5 +226,4 @@ public class TestDstu3Config {
return new PropertySourcesPlaceholderConfigurer();
}
-
}
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java
index 5beaab359db..e6473f15647 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java
@@ -27,11 +27,17 @@ import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
import ca.uhn.fhir.jpa.config.HapiJpaConfig;
import ca.uhn.fhir.jpa.config.r4b.JpaR4BConfig;
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
+import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect;
+import ca.uhn.fhir.jpa.model.entity.StorageSettings;
+import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
+import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.topic.SubscriptionTopicConfig;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
+import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
import ca.uhn.fhir.system.HapiTestSystemProperties;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java
index b88ec150dd6..a95f721fa83 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java
@@ -28,12 +28,18 @@ import ca.uhn.fhir.jpa.config.HapiJpaConfig;
import ca.uhn.fhir.jpa.config.PackageLoaderConfig;
import ca.uhn.fhir.jpa.config.r4.JpaR4Config;
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
+import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect;
+import ca.uhn.fhir.jpa.model.entity.StorageSettings;
import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig;
+import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
+import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
+import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
import ca.uhn.fhir.system.HapiTestSystemProperties;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestSubscriptionMatcherInterceptorConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestSubscriptionMatcherInterceptorConfig.java
new file mode 100644
index 00000000000..6734aec48c8
--- /dev/null
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestSubscriptionMatcherInterceptorConfig.java
@@ -0,0 +1,48 @@
+/*-
+ * #%L
+ * HAPI FHIR JPA Server Test Utilities
+ * %%
+ * Copyright (C) 2014 - 2023 Smile CDR, Inc.
+ * %%
+ * 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%
+ */
+package ca.uhn.fhir.jpa.test.config;
+
+import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
+import ca.uhn.fhir.jpa.subscription.submit.interceptor.SynchronousSubscriptionMatcherInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+/**
+ * Production environments submit modified resources to the subscription processing pipeline asynchronously, ie, a
+ * modified resource is 'planned' for submission which is performed at a later time by a scheduled task.
+ *
+ * The purpose of this class is to provide submission of modified resources during tests since task scheduling required
+ * for asynchronous submission are either disabled or not present in testing context.
+ *
+ * Careful consideration is advised when configuring test context as the SubscriptionMatcherInterceptor Bean instantiated
+ * below will overwrite the Bean provided by class SubscriptionMatcherInterceptorConfig if both configuration classes
+ * are present in the context.
+ */
+@Configuration
+public class TestSubscriptionMatcherInterceptorConfig {
+
+ @Primary
+ @Bean
+ public SubscriptionMatcherInterceptor subscriptionMatcherInterceptor() {
+ return new SynchronousSubscriptionMatcherInterceptor();
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/util/SubscriptionTestUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/util/SubscriptionTestUtil.java
index 27f95748b09..55ac4b30458 100644
--- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/util/SubscriptionTestUtil.java
+++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/util/SubscriptionTestUtil.java
@@ -29,8 +29,8 @@ import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.SubscriptionDeliveringEmailSubscriber;
import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
-import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionSubmitInterceptorLoader;
+import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc;
import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor;
import org.hl7.fhir.dstu2.model.Subscription;
import org.hl7.fhir.instance.model.api.IIdType;
@@ -45,7 +45,7 @@ public class SubscriptionTestUtil {
@Autowired
private SubscriptionSubmitInterceptorLoader mySubscriptionSubmitInterceptorLoader;
@Autowired
- private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
+ private ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc;
@Autowired
private SubscriptionRegistry mySubscriptionRegistry;
@Autowired
@@ -56,7 +56,7 @@ public class SubscriptionTestUtil {
private IInterceptorService myInterceptorRegistry;
public int getExecutorQueueSize() {
- LinkedBlockingChannel channel = mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
+ LinkedBlockingChannel channel = (LinkedBlockingChannel) myResourceModifiedSubmitterSvc.getProcessingChannelForUnitTest();
return channel.getQueueSizeForUnitTest();
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
index 8193cea4513..c0fffc577ff 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
+++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml
index 3d9b44aeffe..1752eb28f89 100644
--- a/hapi-fhir-server-cds-hooks/pom.xml
+++ b/hapi-fhir-server-cds-hooks/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml
index 9ac26e25609..5e1f27d2938 100644
--- a/hapi-fhir-server-mdm/pom.xml
+++ b/hapi-fhir-server-mdm/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml
index a3221fa0bfd..a10cfc8c00e 100644
--- a/hapi-fhir-server-openapi/pom.xml
+++ b/hapi-fhir-server-openapi/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml
index 7c03214ab18..6da4ce5f9ec 100644
--- a/hapi-fhir-server/pom.xml
+++ b/hapi-fhir-server/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java
index 6e3b2380f6b..66283ec0682 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java
@@ -23,8 +23,11 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.model.api.IModelJson;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@@ -229,4 +232,9 @@ public abstract class BaseResourceMessage implements IResourceMessage, IModelJso
return myRestOperationTypeEnum;
}
}
+
+ @VisibleForTesting
+ public Map getAttributes() {
+ return ObjectUtils.defaultIfNull(myAttributes, Collections.emptyMap());
+ }
}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java
index 672ff2b2ca9..c98030e643a 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java
@@ -58,6 +58,9 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im
@JsonIgnore
protected transient String myPayloadType;
+ @JsonIgnore
+ protected String myPayloadVersion;
+
/**
* Constructor
*/
@@ -101,6 +104,10 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im
return myPayloadId;
}
+ public String getPayloadVersion() {
+ return myPayloadVersion;
+ }
+
/**
* @since 5.6.0
*/
@@ -108,6 +115,7 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im
myPayloadId = null;
if (thePayloadId != null) {
myPayloadId = thePayloadId.toUnqualifiedVersionless().getValue();
+ myPayloadVersion = thePayloadId.getVersionIdPart();
}
}
@@ -138,9 +146,11 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im
*/
public IIdType getPayloadId(FhirContext theCtx) {
IIdType retVal = null;
+
if (myPayloadId != null) {
- retVal = theCtx.getVersion().newIdType().setValue(myPayloadId);
+ retVal = theCtx.getVersion().newIdType().setValue(myPayloadId).withVersion(myPayloadVersion);
}
+
return retVal;
}
@@ -172,7 +182,7 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im
return "";
}
- protected void setNewPayload(FhirContext theCtx, IBaseResource thePayload) {
+ public void setNewPayload(FhirContext theCtx, IBaseResource thePayload) {
/*
* References with placeholders would be invalid by the time we get here, and
* would be caught before we even get here. This check is basically a last-ditch
@@ -246,7 +256,7 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im
@Nullable
@Override
public String getMessageKeyOrDefault() {
- return StringUtils.defaultString(super.getMessageKey(), myPayloadId);
+ return StringUtils.defaultString(super.getMessageKeyOrNull(), myPayloadId);
}
public boolean hasPayloadType(FhirContext theFhirContext, @Nonnull String theResourceName) {
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
index c2f66fad694..9d1f0b2a1b3 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
index 7c1dfdc0417..e72e6e7bbf1 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
@@ -21,7 +21,7 @@
ca.uhn.hapi.fhir
hapi-fhir-caching-api
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
index 1ac06d6c405..8619927341b 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir-serviceloaders
ca.uhn.hapi.fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
index c010bdcc872..b00bc09c1a4 100644
--- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
+++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml
@@ -7,7 +7,7 @@
hapi-fhir
ca.uhn.hapi.fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../../pom.xml
diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml
index c9da6381db2..f4766c1c07e 100644
--- a/hapi-fhir-serviceloaders/pom.xml
+++ b/hapi-fhir-serviceloaders/pom.xml
@@ -5,7 +5,7 @@
hapi-deployable-pom
ca.uhn.hapi.fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml
index 6f97b66abb2..d45be5a8738 100644
--- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml
index a676ac2226b..bd44d49aa7f 100644
--- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir-spring-boot-samples
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
hapi-fhir-spring-boot-sample-client-apache
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml
index b6cded3d683..06370d1b38e 100644
--- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir-spring-boot-samples
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml
index 17c056e33c0..64eb1571e38 100644
--- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir-spring-boot-samples
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml
index 49a532b8b60..ef26e60fd55 100644
--- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir-spring-boot
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml
index 6dd06516c4c..b5b79a5dc3f 100644
--- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml
+++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml
index 30afce681af..74f674cc765 100644
--- a/hapi-fhir-spring-boot/pom.xml
+++ b/hapi-fhir-spring-boot/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml
index 2b5e8db2d2b..be8615db926 100644
--- a/hapi-fhir-sql-migrate/pom.xml
+++ b/hapi-fhir-sql-migrate/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml
index 61d4b224d52..4499f2b7866 100644
--- a/hapi-fhir-storage-batch2-jobs/pom.xml
+++ b/hapi-fhir-storage-batch2-jobs/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml
index fa97586fe8f..4a5ca60f520 100644
--- a/hapi-fhir-storage-batch2-test-utilities/pom.xml
+++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml
index 7ec6564731d..c2dd3a7939c 100644
--- a/hapi-fhir-storage-batch2/pom.xml
+++ b/hapi-fhir-storage-batch2/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml
index e6218d15cf4..6c4910ee468 100644
--- a/hapi-fhir-storage-cr/pom.xml
+++ b/hapi-fhir-storage-cr/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml
index 938ed9845e2..89bebd8d4ba 100644
--- a/hapi-fhir-storage-mdm/pom.xml
+++ b/hapi-fhir-storage-mdm/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml
index 59180087fe8..09d74f2fc56 100644
--- a/hapi-fhir-storage-test-utilities/pom.xml
+++ b/hapi-fhir-storage-test-utilities/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml
index 14a2123b723..d1369c1491f 100644
--- a/hapi-fhir-storage/pom.xml
+++ b/hapi-fhir-storage/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/IResourceModifiedConsumer.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/IResourceModifiedConsumer.java
index 094a5c2110c..72bece8f828 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/IResourceModifiedConsumer.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/IResourceModifiedConsumer.java
@@ -20,21 +20,24 @@
package ca.uhn.fhir.jpa.subscription.match.matcher.matching;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
-import ca.uhn.fhir.rest.api.server.RequestDetails;
-import org.hl7.fhir.instance.model.api.IBaseResource;
+import ca.uhn.fhir.subscription.api.IResourceModifiedConsumerWithRetries;
+import org.springframework.messaging.MessageDeliveryException;
+/**
+ * The implementer of this interface should submit the result of an operation on a resource
+ * to the subscription processing pipeline.
+ */
public interface IResourceModifiedConsumer {
/**
+ * Process a message by submitting it to the processing pipeline. The message is assumed to have been successfully
+ * submitted unless a {@link MessageDeliveryException} is thrown by the underlying support. The exception should be allowed to
+ * propagate for client handling and potential re-submission through the {@link IResourceModifiedConsumerWithRetries}.
+ *
+ * @param theMsg The message to submit
+ *
* This is an internal API - Use with caution!
- */
- void submitResourceModified(
- IBaseResource theNewResource,
- ResourceModifiedMessage.OperationTypeEnum theOperationType,
- RequestDetails theRequest);
-
- /**
- * This is an internal API - Use with caution!
+ *
*/
void submitResourceModified(ResourceModifiedMessage theMsg);
}
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java
index 481538ababb..9cd638d2600 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java
@@ -158,6 +158,6 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes
@Nullable
@Override
public String getMessageKeyOrDefault() {
- return StringUtils.defaultString(super.getMessageKey(), myPayloadId);
+ return StringUtils.defaultString(super.getMessageKeyOrNull(), myPayloadId);
}
}
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedConsumerWithRetries.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedConsumerWithRetries.java
new file mode 100644
index 00000000000..5a654569118
--- /dev/null
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedConsumerWithRetries.java
@@ -0,0 +1,41 @@
+package ca.uhn.fhir.subscription.api;
+
+/*-
+ * #%L
+ * HAPI FHIR Storage api
+ * %%
+ * Copyright (C) 2014 - 2023 Smile CDR, Inc.
+ * %%
+ * 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.jpa.model.entity.IPersistedResourceModifiedMessage;
+import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
+
+/**
+ * The implementer of this interface participates in the retry upon failure mechanism for messages submitted
+ * to the subscription processing pipeline.
+ */
+public interface IResourceModifiedConsumerWithRetries {
+
+ /**
+ * The implementer of this method should submit the ResourceModifiedMessage represented the IPersistedResourceModifiedMessage
+ * to a broker (see {@link IResourceModifiedConsumer}) and if submission succeeds, delete the IPersistedResourceModifiedMessage.
+ *
+ * @param thePersistedResourceModifiedMessage A IPersistedResourceModifiedMessage requiring submission.
+ * @return Whether the message was successfully submitted to the broker.
+ */
+ boolean submitPersisedResourceModifiedMessage(
+ IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage);
+}
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java
new file mode 100644
index 00000000000..68aad03a48c
--- /dev/null
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java
@@ -0,0 +1,75 @@
+package ca.uhn.fhir.subscription.api;
+
+/*-
+ * #%L
+ * HAPI FHIR Storage api
+ * %%
+ * Copyright (C) 2014 - 2023 Smile CDR, Inc.
+ * %%
+ * 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.jpa.model.entity.IPersistedResourceModifiedMessage;
+import ca.uhn.fhir.jpa.model.entity.IPersistedResourceModifiedMessagePK;
+import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
+
+import java.util.List;
+
+/**
+ * An implementer of this interface will provide {@link ResourceModifiedMessage} persistence services.
+ *
+ * Client of this interface should persist ResourceModifiedMessage as part of the processing of an operation on
+ * a resource. Upon a successful submission to the subscription pipeline, the persisted message should be deleted.
+ * When submission fails, the message should be left un-altered for re-submission at a later time (see {@link IResourceModifiedConsumerWithRetries}).
+ */
+public interface IResourceModifiedMessagePersistenceSvc {
+
+ /**
+ * Find all persistedResourceModifiedMessage sorted by ascending created dates (oldest to newest).
+ *
+ * @return A sorted list of persistedResourceModifiedMessage needing submission.
+ */
+ List findAllOrderedByCreatedTime();
+
+ /**
+ * Delete a persistedResourceModifiedMessage by its primary key.
+ *
+ * @param thePersistedResourceModifiedMessagePK The primary key of the persistedResourceModifiedMessage to delete.
+ * @return Whether the persistedResourceModifiedMessage pointed to by theResourceModifiedPK
was deleted.
+ */
+ boolean deleteByPK(IPersistedResourceModifiedMessagePK thePersistedResourceModifiedMessagePK);
+
+ /**
+ * Persist a resourceModifiedMessage and return its resulting persisted representation.
+ *
+ * @param theMsg The resourceModifiedMessage to persist.
+ * @return The persisted representation of theMsg
.
+ */
+ IPersistedResourceModifiedMessage persist(ResourceModifiedMessage theMsg);
+
+ /**
+ * Restore a resourceModifiedMessage to its pre persistence representation.
+ *
+ * @param thePersistedResourceModifiedMessage The message needing restoration.
+ * @return The resourceModifiedMessage in its pre persistence form.
+ */
+ ResourceModifiedMessage inflatePersistedResourceModifiedMessage(
+ IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage);
+
+ /**
+ *
+ * @return the number of persisted resourceModifiedMessage.
+ */
+ long getMessagePersistedCount();
+}
diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml
index bd82f9b3ae3..c79c49fee0a 100644
--- a/hapi-fhir-structures-dstu2.1/pom.xml
+++ b/hapi-fhir-structures-dstu2.1/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml
index 3688b898c31..7137a5c425a 100644
--- a/hapi-fhir-structures-dstu2/pom.xml
+++ b/hapi-fhir-structures-dstu2/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml
index 88b56204c6a..b07c22c3270 100644
--- a/hapi-fhir-structures-dstu3/pom.xml
+++ b/hapi-fhir-structures-dstu3/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml
index 6acd5f37162..0c6b2dddb5a 100644
--- a/hapi-fhir-structures-hl7org-dstu2/pom.xml
+++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml
index d48b73b150b..1e6f1df7015 100644
--- a/hapi-fhir-structures-r4/pom.xml
+++ b/hapi-fhir-structures-r4/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml
index 6a1e6535ef7..92c8903d86f 100644
--- a/hapi-fhir-structures-r4b/pom.xml
+++ b/hapi-fhir-structures-r4b/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml
index c0e3b2a14a9..ca8c85d0ea8 100644
--- a/hapi-fhir-structures-r5/pom.xml
+++ b/hapi-fhir-structures-r5/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml
index fb43a3a5932..4a02b8398ac 100644
--- a/hapi-fhir-test-utilities/pom.xml
+++ b/hapi-fhir-test-utilities/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml
index 5d07bf1988a..576da06220a 100644
--- a/hapi-fhir-testpage-overlay/pom.xml
+++ b/hapi-fhir-testpage-overlay/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml
index dd5078130b6..d67ba1e918a 100644
--- a/hapi-fhir-validation-resources-dstu2.1/pom.xml
+++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml
index 0bc9bc9c1db..6b1f99077b0 100644
--- a/hapi-fhir-validation-resources-dstu2/pom.xml
+++ b/hapi-fhir-validation-resources-dstu2/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml
index 7e2c6dd0900..724125dbb3f 100644
--- a/hapi-fhir-validation-resources-dstu3/pom.xml
+++ b/hapi-fhir-validation-resources-dstu3/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml
index 2839ff7c9d8..c4eda275a6a 100644
--- a/hapi-fhir-validation-resources-r4/pom.xml
+++ b/hapi-fhir-validation-resources-r4/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation-resources-r4b/pom.xml b/hapi-fhir-validation-resources-r4b/pom.xml
index d177afa22e1..81e65721346 100644
--- a/hapi-fhir-validation-resources-r4b/pom.xml
+++ b/hapi-fhir-validation-resources-r4b/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml
index 350d22f8560..ef1187e98d9 100644
--- a/hapi-fhir-validation-resources-r5/pom.xml
+++ b/hapi-fhir-validation-resources-r5/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml
index 1918e07a04d..b83747307ec 100644
--- a/hapi-fhir-validation/pom.xml
+++ b/hapi-fhir-validation/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml
index 7d7c6ea61ef..1a291983272 100644
--- a/hapi-tinder-plugin/pom.xml
+++ b/hapi-tinder-plugin/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml
index f3b6f1310c6..c8e857657be 100644
--- a/hapi-tinder-test/pom.xml
+++ b/hapi-tinder-test/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../pom.xml
diff --git a/pom.xml b/pom.xml
index b3323c962f9..27e8637bde8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
ca.uhn.hapi.fhir
hapi-fhir
pom
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
HAPI-FHIR
An open-source implementation of the FHIR specification in Java.
diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
index e8e037360d5..7dac077d23e 100644
--- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
+++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../../pom.xml
diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml
index 9e23f4dcf2d..85b19b77b41 100644
--- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml
+++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../../pom.xml
diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml
index 02ff4353082..499e68e1ee9 100644
--- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml
+++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 6.9.3-SNAPSHOT
+ 6.9.4-SNAPSHOT
../../pom.xml