diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index 520335fca6b..5e8b7d27de7 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml
index a0e76657589..c12e6e57b77 100644
--- a/hapi-fhir-android/pom.xml
+++ b/hapi-fhir-android/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml
index 5fb7d9b771b..fb35d780562 100644
--- a/hapi-fhir-base/pom.xml
+++ b/hapi-fhir-base/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml
index 37459003bbd..4da0f9c1d40 100644
--- a/hapi-fhir-bom/pom.xml
+++ b/hapi-fhir-bom/pom.xml
@@ -4,7 +4,7 @@
4.0.0
ca.uhn.hapi.fhir
hapi-fhir-bom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
pom
HAPI FHIR BOM
@@ -12,7 +12,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml
index 659f30ce0fc..08e0b9c2eaf 100644
--- a/hapi-fhir-checkstyle/pom.xml
+++ b/hapi-fhir-checkstyle/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml
index d5a85e8db5e..eed137d374d 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml
+++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml
index 31efb5e1cdc..de8fa790b0b 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml
+++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-fhir-cli
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml
index bae9d20464c..4fc7c8e427e 100644
--- a/hapi-fhir-cli/pom.xml
+++ b/hapi-fhir-cli/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml
index 0dce45e5b92..ce20d1ab6b2 100644
--- a/hapi-fhir-client-okhttp/pom.xml
+++ b/hapi-fhir-client-okhttp/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml
index 0c8fd1a7c43..638f1ed7be0 100644
--- a/hapi-fhir-client/pom.xml
+++ b/hapi-fhir-client/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml
index 69c806cf9fb..fca0987ddf5 100644
--- a/hapi-fhir-converter/pom.xml
+++ b/hapi-fhir-converter/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml
index 5463e9b5d5a..d90951583dd 100644
--- a/hapi-fhir-dist/pom.xml
+++ b/hapi-fhir-dist/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml
index b253184ccbf..52d01b8dfbf 100644
--- a/hapi-fhir-docs/pom.xml
+++ b/hapi-fhir-docs/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml
index a5d73a949a7..e7c060a760f 100644
--- a/hapi-fhir-jacoco/pom.xml
+++ b/hapi-fhir-jacoco/pom.xml
@@ -11,7 +11,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml
index c992c31b9bb..e00c808a8c9 100644
--- a/hapi-fhir-jaxrsserver-base/pom.xml
+++ b/hapi-fhir-jaxrsserver-base/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml
index dc0e37591be..a530d5d127d 100644
--- a/hapi-fhir-jpa/pom.xml
+++ b/hapi-fhir-jpa/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml
index 9e57f2dedae..3e92c57b452 100644
--- a/hapi-fhir-jpaserver-base/pom.xml
+++ b/hapi-fhir-jpaserver-base/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
index e969a0be649..17c3a08aa50 100644
--- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
+++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml
index 6de7d563dd7..99bed5fc6d0 100644
--- a/hapi-fhir-jpaserver-hfql/pom.xml
+++ b/hapi-fhir-jpaserver-hfql/pom.xml
@@ -3,7 +3,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml
index dbb537202f7..eb66d27cfac 100644
--- a/hapi-fhir-jpaserver-ips/pom.xml
+++ b/hapi-fhir-jpaserver-ips/pom.xml
@@ -3,7 +3,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml
index 6441a09562d..2a8ed1e5e9b 100644
--- a/hapi-fhir-jpaserver-mdm/pom.xml
+++ b/hapi-fhir-jpaserver-mdm/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml
index b44506f53ab..23b8a5f67c5 100644
--- a/hapi-fhir-jpaserver-model/pom.xml
+++ b/hapi-fhir-jpaserver-model/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml
index 751edb8b63f..1cbb7748009 100755
--- a/hapi-fhir-jpaserver-searchparam/pom.xml
+++ b/hapi-fhir-jpaserver-searchparam/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml
index 4e05e2edf03..cc08fd09695 100644
--- a/hapi-fhir-jpaserver-subscription/pom.xml
+++ b/hapi-fhir-jpaserver-subscription/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml
index d51a8940bc0..1ced5f66cf1 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml
index 7fa9c4fbb06..7868b2b95a2 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 8bec81531a4..9a9709161a5 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java
index c2cdca398fb..90654ff9bc1 100644
--- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java
+++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java
@@ -20,6 +20,7 @@ import ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
import ca.uhn.fhir.util.JsonUtil;
import ca.uhn.hapi.fhir.batch2.test.AbstractIJobPersistenceSpecificationTest;
+import ca.uhn.hapi.fhir.batch2.test.configs.SpyOverrideConfig;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import org.junit.jupiter.api.MethodOrderer;
@@ -30,6 +31,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Import;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
@@ -60,6 +62,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@TestMethodOrder(MethodOrderer.MethodName.class)
+@Import(SpyOverrideConfig.class)
public class JpaJobPersistenceImplTest extends BaseJpaR4Test {
public static final String JOB_DEFINITION_ID = "definition-id";
@@ -323,14 +326,19 @@ public class JpaJobPersistenceImplTest extends BaseJpaR4Test {
class Batch2SpecTest extends AbstractIJobPersistenceSpecificationTest {
@Override
- protected PlatformTransactionManager getTxManager() {
+ public PlatformTransactionManager getTxManager() {
return JpaJobPersistenceImplTest.this.getTxManager();
}
@Override
- protected WorkChunk freshFetchWorkChunk(String chunkId) {
+ public WorkChunk freshFetchWorkChunk(String chunkId) {
return JpaJobPersistenceImplTest.this.freshFetchWorkChunk(chunkId);
}
+
+ @Override
+ public void runMaintenancePass() {
+ myBatch2JobHelper.forceRunMaintenancePass();
+ }
}
@Test
diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml
index 77be64734fc..9013e0c6b72 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml
index 545c9c2fbb4..f91d4266b1a 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml
index d92fba3b123..72fcdec2607 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
index a38b2b8c988..434fa5b7fd1 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml
index 4749239fb7f..4e2bbda8f5e 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml
index 366ceb47463..5d85b54408b 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml
index cfaae68b9d9..6371abe0514 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml
index 3d74ccc0870..03bba016e4a 100644
--- a/hapi-fhir-server/pom.xml
+++ b/hapi-fhir-server/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml
index e51308bd2a7..41d0b9e5491 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 32680b37acb..47a646b0196 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
@@ -21,7 +21,7 @@
ca.uhn.hapi.fhir
hapi-fhir-caching-api
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml
index da34ff1770e..1bb5308eec9 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 3e09e7c0160..615bcaede67 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../../pom.xml
diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml
index 3a7bdba7d54..4e9f671c79c 100644
--- a/hapi-fhir-serviceloaders/pom.xml
+++ b/hapi-fhir-serviceloaders/pom.xml
@@ -5,7 +5,7 @@
hapi-deployable-pom
ca.uhn.hapi.fhir
- 7.1.7-SNAPSHOT
+ 7.1.8-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 93b126309c1..5514a27054c 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 7a250b4b4c5..638c3933040 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 a99bea8806d..d3e81bb97d3 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 e3b0cf47a83..6ec3497c477 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 f45a5e76eba..133fa52cfd8 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 d554053161d..e23fe9696d2 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml
index 8b56e169941..25eee371362 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml
index 0827041f3d5..e10a99c7db0 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 bb38f3ccee7..814d926048b 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 69ed1d4dcff..1762a3dbe65 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java
index ce3ea14e2af..230944b128a 100644
--- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java
@@ -20,30 +20,28 @@
package ca.uhn.hapi.fhir.batch2.test;
+import ca.uhn.fhir.batch2.api.IJobMaintenanceService;
import ca.uhn.fhir.batch2.api.IJobPersistence;
import ca.uhn.fhir.batch2.api.RunOutcome;
+import ca.uhn.fhir.batch2.channel.BatchJobSender;
import ca.uhn.fhir.batch2.coordinator.JobDefinitionRegistry;
-import ca.uhn.fhir.batch2.maintenance.JobChunkProgressAccumulator;
-import ca.uhn.fhir.batch2.maintenance.JobInstanceProcessor;
import ca.uhn.fhir.batch2.model.JobDefinition;
import ca.uhn.fhir.batch2.model.JobInstance;
+import ca.uhn.fhir.batch2.model.JobWorkNotification;
import ca.uhn.fhir.batch2.model.StatusEnum;
import ca.uhn.fhir.batch2.model.WorkChunk;
-import ca.uhn.fhir.batch2.model.WorkChunkCompletionEvent;
import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent;
-import ca.uhn.fhir.batch2.model.WorkChunkErrorEvent;
-import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.hapi.fhir.batch2.test.support.TestJobParameters;
import ca.uhn.hapi.fhir.batch2.test.support.TestJobStep2InputType;
import ca.uhn.hapi.fhir.batch2.test.support.TestJobStep3InputType;
-import com.google.common.collect.ImmutableList;
-import org.junit.jupiter.api.BeforeEach;
+import ca.uhn.test.concurrency.PointcutLatch;
+import jakarta.annotation.Nonnull;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.EnumSource;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -53,580 +51,118 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
-import jakarta.annotation.Nonnull;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
import java.util.concurrent.Callable;
import static org.awaitility.Awaitility.await;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.emptyString;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.theInstance;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
/**
* Specification tests for batch2 storage and event system.
* These tests are abstract, and do not depend on JPA.
* Test setups should use the public batch2 api to create scenarios.
*/
-public abstract class AbstractIJobPersistenceSpecificationTest {
- private static final Logger ourLog = LoggerFactory.getLogger(AbstractIJobPersistenceSpecificationTest.class);
+public abstract class AbstractIJobPersistenceSpecificationTest implements IInProgressActionsTests, IInstanceStateTransitions, ITestFixture, WorkChunkTestConstants {
- public static final String JOB_DEFINITION_ID = "definition-id";
- public static final String TARGET_STEP_ID = "step-id";
- public static final String DEF_CHUNK_ID = "definition-chunkId";
- public static final String STEP_CHUNK_ID = "step-chunkId";
- public static final int JOB_DEF_VER = 1;
- public static final int SEQUENCE_NUMBER = 1;
- public static final String CHUNK_DATA = "{\"key\":\"value\"}";
- public static final String ERROR_MESSAGE_A = "This is an error message: A";
- public static final String ERROR_MESSAGE_B = "This is a different error message: B";
- public static final String ERROR_MESSAGE_C = "This is a different error message: C";
+ private static final Logger ourLog = LoggerFactory.getLogger(AbstractIJobPersistenceSpecificationTest.class);
@Autowired
private IJobPersistence mySvc;
- @Nested
- class WorkChunkStorage {
+ @Autowired
+ private JobDefinitionRegistry myJobDefinitionRegistry;
- @Test
- public void testStoreAndFetchWorkChunk_NoData() {
- JobInstance instance = createInstance();
- String instanceId = mySvc.storeNewInstance(instance);
+ @Autowired
+ private PlatformTransactionManager myTransactionManager;
- String id = storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, 0, null);
+ @Autowired
+ private IJobMaintenanceService myMaintenanceService;
- WorkChunk chunk = mySvc.onWorkChunkDequeue(id).orElseThrow(IllegalArgumentException::new);
- assertNull(chunk.getData());
- }
+ @Autowired
+ private BatchJobSender myBatchJobSender;
- @Test
- public void testStoreAndFetchWorkChunk_WithData() {
- JobInstance instance = createInstance();
- String instanceId = mySvc.storeNewInstance(instance);
-
- String id = storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, 0, CHUNK_DATA);
- assertNotNull(id);
- runInTransaction(() -> assertEquals(WorkChunkStatusEnum.QUEUED, freshFetchWorkChunk(id).getStatus()));
-
- WorkChunk chunk = mySvc.onWorkChunkDequeue(id).orElseThrow(IllegalArgumentException::new);
- assertEquals(36, chunk.getInstanceId().length());
- assertEquals(JOB_DEFINITION_ID, chunk.getJobDefinitionId());
- assertEquals(JOB_DEF_VER, chunk.getJobDefinitionVersion());
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
- assertEquals(CHUNK_DATA, chunk.getData());
-
- runInTransaction(() -> assertEquals(WorkChunkStatusEnum.IN_PROGRESS, freshFetchWorkChunk(id).getStatus()));
- }
-
- /**
- * Should match the diagram in batch2_states.md
- * @see hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_batch/batch2_states.md
- */
- @Nested
- class StateTransitions {
-
- private String myInstanceId;
- private String myChunkId;
-
- @BeforeEach
- void setUp() {
- JobInstance jobInstance = createInstance();
- myInstanceId = mySvc.storeNewInstance(jobInstance);
-
- }
-
- private String createChunk() {
- return storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, myInstanceId, 0, CHUNK_DATA);
- }
-
- @Test
- public void chunkCreation_isQueued() {
-
- myChunkId = createChunk();
-
- WorkChunk fetchedWorkChunk = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.QUEUED, fetchedWorkChunk.getStatus(), "New chunks are QUEUED");
- }
-
- @Test
- public void chunkReceived_queuedToInProgress() {
-
- myChunkId = createChunk();
-
- // the worker has received the chunk, and marks it started.
- WorkChunk chunk = mySvc.onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new);
-
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
- assertEquals(CHUNK_DATA, chunk.getData());
-
- // verify the db was updated too
- WorkChunk fetchedWorkChunk = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, fetchedWorkChunk.getStatus());
- }
-
- @Nested
- class InProgressActions {
- @BeforeEach
- void setUp() {
- // setup - the worker has received the chunk, and has marked it IN_PROGRESS.
- myChunkId = createChunk();
- mySvc.onWorkChunkDequeue(myChunkId);
- }
-
- @Test
- public void processingOk_inProgressToSuccess_clearsDataSavesRecordCount() {
-
- // execution ok
- mySvc.onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 0));
-
- // verify the db was updated
- var workChunkEntity = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.COMPLETED, workChunkEntity.getStatus());
- assertNull(workChunkEntity.getData());
- assertEquals(3, workChunkEntity.getRecordsProcessed());
- assertNull(workChunkEntity.getErrorMessage());
- assertEquals(0, workChunkEntity.getErrorCount());
- }
-
- @Test
- public void processingRetryableError_inProgressToError_bumpsCountRecordsMessage() {
-
- // execution had a retryable error
- mySvc.onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_A));
-
- // verify the db was updated
- var workChunkEntity = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.ERRORED, workChunkEntity.getStatus());
- assertEquals(ERROR_MESSAGE_A, workChunkEntity.getErrorMessage());
- assertEquals(1, workChunkEntity.getErrorCount());
- }
-
- @Test
- public void processingFailure_inProgressToFailed() {
-
- // execution had a failure
- mySvc.onWorkChunkFailed(myChunkId, "some error");
-
- // verify the db was updated
- var workChunkEntity = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.FAILED, workChunkEntity.getStatus());
- assertEquals("some error", workChunkEntity.getErrorMessage());
- }
- }
-
- @Nested
- class ErrorActions {
- public static final String FIRST_ERROR_MESSAGE = ERROR_MESSAGE_A;
- @BeforeEach
- void setUp() {
- // setup - the worker has received the chunk, and has marked it IN_PROGRESS.
- myChunkId = createChunk();
- mySvc.onWorkChunkDequeue(myChunkId);
- // execution had a retryable error
- mySvc.onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE));
- }
-
- /**
- * The consumer will retry after a retryable error is thrown
- */
- @Test
- void errorRetry_errorToInProgress() {
-
- // when consumer restarts chunk
- WorkChunk chunk = mySvc.onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new);
-
- // then
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
-
- // verify the db state, error message, and error count
- var workChunkEntity = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, workChunkEntity.getStatus());
- assertEquals(FIRST_ERROR_MESSAGE, workChunkEntity.getErrorMessage(), "Original error message kept");
- assertEquals(1, workChunkEntity.getErrorCount(), "error count kept");
- }
-
- @Test
- void errorRetry_repeatError_increasesErrorCount() {
- // setup - the consumer is re-trying, and marks it IN_PROGRESS
- mySvc.onWorkChunkDequeue(myChunkId);
-
-
- // when another error happens
- mySvc.onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B));
-
-
- // verify the state, new message, and error count
- var workChunkEntity = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.ERRORED, workChunkEntity.getStatus());
- assertEquals(ERROR_MESSAGE_B, workChunkEntity.getErrorMessage(), "new error message");
- assertEquals(2, workChunkEntity.getErrorCount(), "error count inc");
- }
-
- @Test
- void errorThenRetryAndComplete_addsErrorCounts() {
- // setup - the consumer is re-trying, and marks it IN_PROGRESS
- mySvc.onWorkChunkDequeue(myChunkId);
-
- // then it completes ok.
- mySvc.onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 1));
-
- // verify the state, new message, and error count
- var workChunkEntity = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.COMPLETED, workChunkEntity.getStatus());
- assertEquals(FIRST_ERROR_MESSAGE, workChunkEntity.getErrorMessage(), "Error message kept.");
- assertEquals(2, workChunkEntity.getErrorCount(), "error combined with earlier error");
- }
-
- @Test
- void errorRetry_maxErrors_movesToFailed() {
- // we start with 1 error already
-
- // 2nd try
- mySvc.onWorkChunkDequeue(myChunkId);
- mySvc.onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B));
- var chunk = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.ERRORED, chunk.getStatus());
- assertEquals(2, chunk.getErrorCount());
-
- // 3rd try
- mySvc.onWorkChunkDequeue(myChunkId);
- mySvc.onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B));
- chunk = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.ERRORED, chunk.getStatus());
- assertEquals(3, chunk.getErrorCount());
-
- // 4th try
- mySvc.onWorkChunkDequeue(myChunkId);
- mySvc.onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_C));
- chunk = freshFetchWorkChunk(myChunkId);
- assertEquals(WorkChunkStatusEnum.FAILED, chunk.getStatus());
- assertEquals(4, chunk.getErrorCount());
- assertThat("Error message contains last error", chunk.getErrorMessage(), containsString(ERROR_MESSAGE_C));
- assertThat("Error message contains error count and complaint", chunk.getErrorMessage(), containsString("many errors: 4"));
- }
- }
- }
-
- @Test
- public void testMarkChunkAsCompleted_Success() {
- JobInstance instance = createInstance();
- String instanceId = mySvc.storeNewInstance(instance);
- String chunkId = storeWorkChunk(DEF_CHUNK_ID, STEP_CHUNK_ID, instanceId, SEQUENCE_NUMBER, CHUNK_DATA);
- assertNotNull(chunkId);
-
- runInTransaction(() -> assertEquals(WorkChunkStatusEnum.QUEUED, freshFetchWorkChunk(chunkId).getStatus()));
-
- sleepUntilTimeChanges();
-
- WorkChunk chunk = mySvc.onWorkChunkDequeue(chunkId).orElseThrow(IllegalArgumentException::new);
- assertEquals(SEQUENCE_NUMBER, chunk.getSequence());
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
- assertNotNull(chunk.getCreateTime());
- assertNotNull(chunk.getStartTime());
- assertNull(chunk.getEndTime());
- assertNull(chunk.getRecordsProcessed());
- assertNotNull(chunk.getData());
- runInTransaction(() -> assertEquals(WorkChunkStatusEnum.IN_PROGRESS, freshFetchWorkChunk(chunkId).getStatus()));
-
- sleepUntilTimeChanges();
-
- runInTransaction(() -> mySvc.onWorkChunkCompletion(new WorkChunkCompletionEvent(chunkId, 50, 0)));
-
- WorkChunk entity = freshFetchWorkChunk(chunkId);
- assertEquals(WorkChunkStatusEnum.COMPLETED, entity.getStatus());
- assertEquals(50, entity.getRecordsProcessed());
- assertNotNull(entity.getCreateTime());
- assertNotNull(entity.getStartTime());
- assertNotNull(entity.getEndTime());
- assertNull(entity.getData());
- assertTrue(entity.getCreateTime().getTime() < entity.getStartTime().getTime());
- assertTrue(entity.getStartTime().getTime() < entity.getEndTime().getTime());
- }
-
-
- @Test
- public void testMarkChunkAsCompleted_Error() {
- JobInstance instance = createInstance();
- String instanceId = mySvc.storeNewInstance(instance);
- String chunkId = storeWorkChunk(DEF_CHUNK_ID, STEP_CHUNK_ID, instanceId, SEQUENCE_NUMBER, null);
- assertNotNull(chunkId);
-
- runInTransaction(() -> assertEquals(WorkChunkStatusEnum.QUEUED, freshFetchWorkChunk(chunkId).getStatus()));
-
- sleepUntilTimeChanges();
-
- WorkChunk chunk = mySvc.onWorkChunkDequeue(chunkId).orElseThrow(IllegalArgumentException::new);
- assertEquals(SEQUENCE_NUMBER, chunk.getSequence());
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
-
- sleepUntilTimeChanges();
-
- WorkChunkErrorEvent request = new WorkChunkErrorEvent(chunkId, ERROR_MESSAGE_A);
- mySvc.onWorkChunkError(request);
- runInTransaction(() -> {
- WorkChunk entity = freshFetchWorkChunk(chunkId);
- assertEquals(WorkChunkStatusEnum.ERRORED, entity.getStatus());
- assertEquals(ERROR_MESSAGE_A, entity.getErrorMessage());
- assertNotNull(entity.getCreateTime());
- assertNotNull(entity.getStartTime());
- assertNotNull(entity.getEndTime());
- assertEquals(1, entity.getErrorCount());
- assertTrue(entity.getCreateTime().getTime() < entity.getStartTime().getTime());
- assertTrue(entity.getStartTime().getTime() < entity.getEndTime().getTime());
- });
-
- // Mark errored again
-
- WorkChunkErrorEvent request2 = new WorkChunkErrorEvent(chunkId, "This is an error message 2");
- mySvc.onWorkChunkError(request2);
- runInTransaction(() -> {
- WorkChunk entity = freshFetchWorkChunk(chunkId);
- assertEquals(WorkChunkStatusEnum.ERRORED, entity.getStatus());
- assertEquals("This is an error message 2", entity.getErrorMessage());
- assertNotNull(entity.getCreateTime());
- assertNotNull(entity.getStartTime());
- assertNotNull(entity.getEndTime());
- assertEquals(2, entity.getErrorCount());
- assertTrue(entity.getCreateTime().getTime() < entity.getStartTime().getTime());
- assertTrue(entity.getStartTime().getTime() < entity.getEndTime().getTime());
- });
-
- List chunks = ImmutableList.copyOf(mySvc.fetchAllWorkChunksIterator(instanceId, true));
- assertEquals(1, chunks.size());
- assertEquals(2, chunks.get(0).getErrorCount());
- }
-
- @Test
- public void testMarkChunkAsCompleted_Fail() {
- JobInstance instance = createInstance();
- String instanceId = mySvc.storeNewInstance(instance);
- String chunkId = storeWorkChunk(DEF_CHUNK_ID, STEP_CHUNK_ID, instanceId, SEQUENCE_NUMBER, null);
- assertNotNull(chunkId);
-
- runInTransaction(() -> assertEquals(WorkChunkStatusEnum.QUEUED, freshFetchWorkChunk(chunkId).getStatus()));
-
- sleepUntilTimeChanges();
-
- WorkChunk chunk = mySvc.onWorkChunkDequeue(chunkId).orElseThrow(IllegalArgumentException::new);
- assertEquals(SEQUENCE_NUMBER, chunk.getSequence());
- assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
-
- sleepUntilTimeChanges();
-
- mySvc.onWorkChunkFailed(chunkId, "This is an error message");
- runInTransaction(() -> {
- WorkChunk entity = freshFetchWorkChunk(chunkId);
- assertEquals(WorkChunkStatusEnum.FAILED, entity.getStatus());
- assertEquals("This is an error message", entity.getErrorMessage());
- assertNotNull(entity.getCreateTime());
- assertNotNull(entity.getStartTime());
- assertNotNull(entity.getEndTime());
- assertTrue(entity.getCreateTime().getTime() < entity.getStartTime().getTime());
- assertTrue(entity.getStartTime().getTime() < entity.getEndTime().getTime());
- });
- }
-
- @Test
- public void markWorkChunksWithStatusAndWipeData_marksMultipleChunksWithStatus_asExpected() {
- JobInstance instance = createInstance();
- String instanceId = mySvc.storeNewInstance(instance);
- ArrayList chunkIds = new ArrayList<>();
- for (int i = 0; i < 10; i++) {
- WorkChunkCreateEvent chunk = new WorkChunkCreateEvent(
- "defId",
- 1,
- "stepId",
- instanceId,
- 0,
- "{}"
- );
- String id = mySvc.onWorkChunkCreate(chunk);
- chunkIds.add(id);
- }
-
- runInTransaction(() -> mySvc.markWorkChunksWithStatusAndWipeData(instance.getInstanceId(), chunkIds, WorkChunkStatusEnum.COMPLETED, null));
-
- Iterator reducedChunks = mySvc.fetchAllWorkChunksIterator(instanceId, true);
-
- while (reducedChunks.hasNext()) {
- WorkChunk reducedChunk = reducedChunks.next();
- assertTrue(chunkIds.contains(reducedChunk.getId()));
- assertEquals(WorkChunkStatusEnum.COMPLETED, reducedChunk.getStatus());
- }
- }
+ public PlatformTransactionManager getTransactionManager() {
+ return myTransactionManager;
}
- /**
- * Test
- * * @see hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_batch/batch2_states.md
- */
- @Nested
- class InstanceStateTransitions {
-
- @Test
- void createInstance_createsInQueuedWithChunk() {
- // given
- JobDefinition> jd = withJobDefinition();
-
- // when
- IJobPersistence.CreateResult createResult =
- newTxTemplate().execute(status->
- mySvc.onCreateWithFirstChunk(jd, "{}"));
-
- // then
- ourLog.info("job and chunk created {}", createResult);
- assertNotNull(createResult);
- assertThat(createResult.jobInstanceId, not(emptyString()));
- assertThat(createResult.workChunkId, not(emptyString()));
-
- JobInstance jobInstance = freshFetchJobInstance(createResult.jobInstanceId);
- assertThat(jobInstance.getStatus(), equalTo(StatusEnum.QUEUED));
- assertThat(jobInstance.getParameters(), equalTo("{}"));
-
- WorkChunk firstChunk = freshFetchWorkChunk(createResult.workChunkId);
- assertThat(firstChunk.getStatus(), equalTo(WorkChunkStatusEnum.QUEUED));
- assertNull(firstChunk.getData(), "First chunk data is null - only uses parameters");
- }
-
- @Test
- void testCreateInstance_firstChunkDequeued_movesToInProgress() {
- // given
- JobDefinition> jd = withJobDefinition();
- IJobPersistence.CreateResult createResult = newTxTemplate().execute(status->
- mySvc.onCreateWithFirstChunk(jd, "{}"));
- assertNotNull(createResult);
-
- // when
- newTxTemplate().execute(status -> mySvc.onChunkDequeued(createResult.jobInstanceId));
-
- // then
- JobInstance jobInstance = freshFetchJobInstance(createResult.jobInstanceId);
- assertThat(jobInstance.getStatus(), equalTo(StatusEnum.IN_PROGRESS));
- }
-
-
-
- @ParameterizedTest
- @EnumSource(StatusEnum.class)
- void cancelRequest_cancelsJob_whenNotFinalState(StatusEnum theState) {
- // given
- JobInstance cancelledInstance = createInstance();
- cancelledInstance.setStatus(theState);
- String instanceId1 = mySvc.storeNewInstance(cancelledInstance);
- mySvc.cancelInstance(instanceId1);
-
- JobInstance normalInstance = createInstance();
- normalInstance.setStatus(theState);
- String instanceId2 = mySvc.storeNewInstance(normalInstance);
-
- JobDefinitionRegistry jobDefinitionRegistry = new JobDefinitionRegistry();
- jobDefinitionRegistry.addJobDefinitionIfNotRegistered(withJobDefinition());
-
-
- // when
- runInTransaction(()-> new JobInstanceProcessor(mySvc, null, instanceId1, new JobChunkProgressAccumulator(), null, jobDefinitionRegistry)
- .process());
-
-
- // then
- JobInstance freshInstance1 = mySvc.fetchInstance(instanceId1).orElseThrow();
- if (theState.isCancellable()) {
- assertEquals(StatusEnum.CANCELLED, freshInstance1.getStatus(), "cancel request processed");
- assertThat(freshInstance1.getErrorMessage(), containsString("Job instance cancelled"));
- } else {
- assertEquals(theState, freshInstance1.getStatus(), "cancel request ignored - state unchanged");
- assertNull(freshInstance1.getErrorMessage(), "no error message");
- }
- JobInstance freshInstance2 = mySvc.fetchInstance(instanceId2).orElseThrow();
- assertEquals(theState, freshInstance2.getStatus(), "cancel request ignored - cancelled not set");
- }
+ public IJobPersistence getSvc() {
+ return mySvc;
}
- @Test
- void testDeleteChunksAndMarkInstanceAsChunksPurged_doesWhatItSays() {
- // given
- JobDefinition> jd = withJobDefinition();
- IJobPersistence.CreateResult createResult = newTxTemplate().execute(status->
- mySvc.onCreateWithFirstChunk(jd, "{}"));
- String instanceId = createResult.jobInstanceId;
- for (int i = 0; i < 10; i++) {
- storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, i, CHUNK_DATA);
- }
- JobInstance readback = freshFetchJobInstance(instanceId);
- assertFalse(readback.isWorkChunksPurged());
- assertTrue(mySvc.fetchAllWorkChunksIterator(instanceId, true).hasNext(), "has chunk");
-
- // when
- mySvc.deleteChunksAndMarkInstanceAsChunksPurged(instanceId);
-
- // then
- readback = freshFetchJobInstance(instanceId);
- assertTrue(readback.isWorkChunksPurged(), "purged set");
- assertFalse(mySvc.fetchAllWorkChunksIterator(instanceId, true).hasNext(), "chunks gone");
- }
-
- @Test
- void testInstanceUpdate_modifierApplied() {
- // given
- String instanceId = mySvc.storeNewInstance(createInstance());
-
- // when
- mySvc.updateInstance(instanceId, instance ->{
- instance.setErrorCount(42);
- return true;
- });
-
- // then
- JobInstance jobInstance = freshFetchJobInstance(instanceId);
- assertEquals(42, jobInstance.getErrorCount());
- }
-
- @Test
- void testInstanceUpdate_modifierNotAppliedWhenPredicateReturnsFalse() {
- // given
- JobInstance instance1 = createInstance();
- boolean initialValue = true;
- instance1.setFastTracking(initialValue);
- String instanceId = mySvc.storeNewInstance(instance1);
-
- // when
- mySvc.updateInstance(instanceId, instance ->{
- instance.setFastTracking(false);
- return false;
- });
-
- // then
- JobInstance jobInstance = freshFetchJobInstance(instanceId);
- assertEquals(initialValue, jobInstance.isFastTracking());
- }
-
- private JobDefinition withJobDefinition() {
- return JobDefinition.newBuilder()
- .setJobDefinitionId(JOB_DEFINITION_ID)
+ public JobDefinition withJobDefinition(boolean theIsGatedBoolean) {
+ JobDefinition.Builder builder = JobDefinition.newBuilder()
+ .setJobDefinitionId(theIsGatedBoolean ? GATED_JOB_DEFINITION_ID : JOB_DEFINITION_ID)
.setJobDefinitionVersion(JOB_DEF_VER)
.setJobDescription("A job description")
.setParametersType(TestJobParameters.class)
.addFirstStep(TARGET_STEP_ID, "the first step", TestJobStep2InputType.class, (theStepExecutionDetails, theDataSink) -> new RunOutcome(0))
.addIntermediateStep("2nd-step-id", "the second step", TestJobStep3InputType.class, (theStepExecutionDetails, theDataSink) -> new RunOutcome(0))
- .addLastStep("last-step-id", "the final step", (theStepExecutionDetails, theDataSink) -> new RunOutcome(0))
- .build();
+ .addLastStep("last-step-id", "the final step", (theStepExecutionDetails, theDataSink) -> new RunOutcome(0));
+ if (theIsGatedBoolean) {
+ builder.gatedExecution();
+ }
+ return builder.build();
}
+ @AfterEach
+ public void after() {
+ myJobDefinitionRegistry.removeJobDefinition(JOB_DEFINITION_ID, JOB_DEF_VER);
+
+ // clear invocations on the batch sender from previous jobs that might be
+ // kicking around
+ Mockito.clearInvocations(myBatchJobSender);
+ }
+
+ @Override
+ public ITestFixture getTestManager() {
+ return this;
+ }
+
+ @Override
+ public void enableMaintenanceRunner(boolean theToEnable) {
+ myMaintenanceService.enableMaintenancePass(theToEnable);
+ }
+
+ @Nested
+ class WorkChunkStorage implements IWorkChunkStorageTests {
+
+ @Override
+ public ITestFixture getTestManager() {
+ return AbstractIJobPersistenceSpecificationTest.this;
+ }
+
+ @Nested
+ class StateTransitions implements IWorkChunkStateTransitions {
+
+ @Override
+ public ITestFixture getTestManager() {
+ return AbstractIJobPersistenceSpecificationTest.this;
+ }
+
+ @Nested
+ class ErrorActions implements IWorkChunkErrorActionsTests {
+
+ @Override
+ public ITestFixture getTestManager() {
+ return AbstractIJobPersistenceSpecificationTest.this;
+ }
+ }
+ }
+ }
@Nonnull
- private JobInstance createInstance() {
+ public JobInstance createInstance(JobDefinition> theJobDefinition) {
+ JobDefinition> jobDefinition = theJobDefinition == null ? withJobDefinition(false)
+ : theJobDefinition;
+ if (myJobDefinitionRegistry.getJobDefinition(jobDefinition.getJobDefinitionId(), jobDefinition.getJobDefinitionVersion()).isEmpty()) {
+ myJobDefinitionRegistry.addJobDefinition(jobDefinition);
+ }
+
JobInstance instance = new JobInstance();
- instance.setJobDefinitionId(JOB_DEFINITION_ID);
+ instance.setJobDefinitionId(jobDefinition.getJobDefinitionId());
+ instance.setJobDefinitionVersion(jobDefinition.getJobDefinitionVersion());
instance.setStatus(StatusEnum.QUEUED);
instance.setJobDefinitionVersion(JOB_DEF_VER);
instance.setParameters(CHUNK_DATA);
@@ -634,18 +170,20 @@ public abstract class AbstractIJobPersistenceSpecificationTest {
return instance;
}
- private String storeWorkChunk(String theJobDefinitionId, String theTargetStepId, String theInstanceId, int theSequence, String theSerializedData) {
+ public String storeWorkChunk(String theJobDefinitionId, String theTargetStepId, String theInstanceId, int theSequence, String theSerializedData) {
WorkChunkCreateEvent batchWorkChunk = new WorkChunkCreateEvent(theJobDefinitionId, JOB_DEF_VER, theTargetStepId, theInstanceId, theSequence, theSerializedData);
return mySvc.onWorkChunkCreate(batchWorkChunk);
}
+ public abstract PlatformTransactionManager getTxManager();
- protected abstract PlatformTransactionManager getTxManager();
- protected abstract WorkChunk freshFetchWorkChunk(String theChunkId);
- protected JobInstance freshFetchJobInstance(String theInstanceId) {
+ public JobInstance freshFetchJobInstance(String theInstanceId) {
return runInTransaction(() -> mySvc.fetchInstance(theInstanceId).orElseThrow());
}
+ @Override
+ public abstract void runMaintenancePass();
+
public TransactionTemplate newTxTemplate() {
TransactionTemplate retVal = new TransactionTemplate(getTxManager());
retVal.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
@@ -681,6 +219,39 @@ public abstract class AbstractIJobPersistenceSpecificationTest {
await().until(() -> sw.getMillis() > 0);
}
+ public String createAndStoreJobInstance(JobDefinition> theJobDefinition) {
+ JobInstance jobInstance = createInstance(theJobDefinition);
+ return mySvc.storeNewInstance(jobInstance);
+ }
+ public String createAndDequeueWorkChunk(String theJobInstanceId) {
+ String chunkId = createChunk(theJobInstanceId);
+ mySvc.onWorkChunkDequeue(chunkId);
+ return chunkId;
+ }
+ @Override
+ public abstract WorkChunk freshFetchWorkChunk(String theChunkId);
+
+ public String createChunk(String theInstanceId) {
+ return storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, theInstanceId, 0, CHUNK_DATA);
+ }
+
+ public PointcutLatch disableWorkChunkMessageHandler() {
+ PointcutLatch latch = new PointcutLatch(new Exception().getStackTrace()[0].getMethodName());
+
+ doAnswer(a -> {
+ latch.call(1);
+ return Void.class;
+ }).when(myBatchJobSender).sendWorkChannelMessage(any(JobWorkNotification.class));
+ return latch;
+ }
+
+ public void verifyWorkChunkMessageHandlerCalled(PointcutLatch theSendingLatch, int theNumberOfTimes) throws InterruptedException {
+ theSendingLatch.awaitExpected();
+ ArgumentCaptor notificationCaptor = ArgumentCaptor.forClass(JobWorkNotification.class);
+
+ verify(myBatchJobSender, times(theNumberOfTimes))
+ .sendWorkChannelMessage(notificationCaptor.capture());
+ }
}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInProgressActionsTests.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInProgressActionsTests.java
new file mode 100644
index 00000000000..eda69e384ac
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInProgressActionsTests.java
@@ -0,0 +1,55 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+import ca.uhn.fhir.batch2.model.WorkChunkCompletionEvent;
+import ca.uhn.fhir.batch2.model.WorkChunkErrorEvent;
+import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public interface IInProgressActionsTests extends IWorkChunkCommon, WorkChunkTestConstants {
+
+ @Test
+ default void processingOk_inProgressToSuccess_clearsDataSavesRecordCount() {
+ String jobId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId);
+ // execution ok
+ getTestManager().getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 0));
+
+ // verify the db was updated
+ var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.COMPLETED, workChunkEntity.getStatus());
+ assertNull(workChunkEntity.getData());
+ assertEquals(3, workChunkEntity.getRecordsProcessed());
+ assertNull(workChunkEntity.getErrorMessage());
+ assertEquals(0, workChunkEntity.getErrorCount());
+ }
+
+ @Test
+ default void processingRetryableError_inProgressToError_bumpsCountRecordsMessage() {
+ String jobId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId);
+ // execution had a retryable error
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_A));
+
+ // verify the db was updated
+ var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.ERRORED, workChunkEntity.getStatus());
+ assertEquals(ERROR_MESSAGE_A, workChunkEntity.getErrorMessage());
+ assertEquals(1, workChunkEntity.getErrorCount());
+ }
+
+ @Test
+ default void processingFailure_inProgressToFailed() {
+ String jobId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId);
+ // execution had a failure
+ getTestManager().getSvc().onWorkChunkFailed(myChunkId, "some error");
+
+ // verify the db was updated
+ var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.FAILED, workChunkEntity.getStatus());
+ assertEquals("some error", workChunkEntity.getErrorMessage());
+ }
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInstanceStateTransitions.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInstanceStateTransitions.java
new file mode 100644
index 00000000000..57909eaecd1
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IInstanceStateTransitions.java
@@ -0,0 +1,82 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+import ca.uhn.fhir.batch2.api.IJobPersistence;
+import ca.uhn.fhir.batch2.coordinator.JobDefinitionRegistry;
+import ca.uhn.fhir.batch2.maintenance.JobChunkProgressAccumulator;
+import ca.uhn.fhir.batch2.maintenance.JobInstanceProcessor;
+import ca.uhn.fhir.batch2.model.JobDefinition;
+import ca.uhn.fhir.batch2.model.JobInstance;
+import ca.uhn.fhir.batch2.model.StatusEnum;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public interface IInstanceStateTransitions extends IWorkChunkCommon, WorkChunkTestConstants {
+ Logger ourLog = LoggerFactory.getLogger(IInstanceStateTransitions.class);
+
+ @Test
+ default void testCreateInstance_firstChunkDequeued_movesToInProgress() {
+ // given
+ JobDefinition> jd = getTestManager().withJobDefinition(false);
+ IJobPersistence.CreateResult createResult = getTestManager().newTxTemplate().execute(status->
+ getTestManager().getSvc().onCreateWithFirstChunk(jd, "{}"));
+ assertNotNull(createResult);
+
+ // when
+ getTestManager().newTxTemplate().execute(status -> getTestManager().getSvc().onChunkDequeued(createResult.jobInstanceId));
+
+ // then
+ JobInstance jobInstance = getTestManager().freshFetchJobInstance(createResult.jobInstanceId);
+ assertThat(jobInstance.getStatus(), equalTo(StatusEnum.IN_PROGRESS));
+ }
+
+ @ParameterizedTest
+ @EnumSource(StatusEnum.class)
+ default void cancelRequest_cancelsJob_whenNotFinalState(StatusEnum theState) {
+ // given
+ JobInstance cancelledInstance = createInstance();
+ cancelledInstance.setStatus(theState);
+ String instanceId1 = getTestManager().getSvc().storeNewInstance(cancelledInstance);
+ getTestManager().getSvc().cancelInstance(instanceId1);
+
+ JobInstance normalInstance = createInstance();
+ normalInstance.setStatus(theState);
+ String instanceId2 = getTestManager().getSvc().storeNewInstance(normalInstance);
+
+ JobDefinitionRegistry jobDefinitionRegistry = new JobDefinitionRegistry();
+ jobDefinitionRegistry.addJobDefinitionIfNotRegistered(getTestManager().withJobDefinition(false));
+
+ // when
+ getTestManager().runInTransaction(()-> {
+ new JobInstanceProcessor(
+ getTestManager().getSvc(),
+ null,
+ instanceId1,
+ new JobChunkProgressAccumulator(),
+ null,
+ jobDefinitionRegistry
+ ).process();
+ });
+
+ // then
+ JobInstance freshInstance1 = getTestManager().getSvc().fetchInstance(instanceId1).orElseThrow();
+ if (theState.isCancellable()) {
+ assertEquals(StatusEnum.CANCELLED, freshInstance1.getStatus(), "cancel request processed");
+ assertThat(freshInstance1.getErrorMessage(), containsString("Job instance cancelled"));
+ } else {
+ assertEquals(theState, freshInstance1.getStatus(), "cancel request ignored - state unchanged");
+ assertNull(freshInstance1.getErrorMessage(), "no error message");
+ }
+ JobInstance freshInstance2 = getTestManager().getSvc().fetchInstance(instanceId2).orElseThrow();
+ assertEquals(theState, freshInstance2.getStatus(), "cancel request ignored - cancelled not set");
+ }
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/ITestFixture.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/ITestFixture.java
new file mode 100644
index 00000000000..40089ad9f8c
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/ITestFixture.java
@@ -0,0 +1,65 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+import ca.uhn.fhir.batch2.api.IJobPersistence;
+import ca.uhn.fhir.batch2.model.JobDefinition;
+import ca.uhn.fhir.batch2.model.JobInstance;
+import ca.uhn.fhir.batch2.model.WorkChunk;
+import ca.uhn.hapi.fhir.batch2.test.support.TestJobParameters;
+import ca.uhn.test.concurrency.PointcutLatch;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.support.TransactionTemplate;
+
+public interface ITestFixture {
+
+ String createAndStoreJobInstance(JobDefinition> theJobDefinition);
+
+ String createAndDequeueWorkChunk(String theJobInstanceId);
+
+ WorkChunk freshFetchWorkChunk(String theChunkId);
+
+ String storeWorkChunk(String theJobDefinitionId, String theTargetStepId, String theInstanceId, int theSequence, String theSerializedData);
+
+ void runInTransaction(Runnable theRunnable);
+
+ void sleepUntilTimeChanges();
+
+ JobDefinition withJobDefinition(boolean theIsGatedJob);
+
+ TransactionTemplate newTxTemplate();
+
+ JobInstance freshFetchJobInstance(String theInstanceId);
+
+ void runMaintenancePass();
+
+ PlatformTransactionManager getTransactionManager();
+
+ IJobPersistence getSvc();
+
+ /**
+ * This assumes a creation of JOB_DEFINITION already
+ * @param theJobInstanceId
+ * @return
+ */
+ String createChunk(String theJobInstanceId);
+
+ /**
+ * Enable/disable the maintenance runner (So it doesn't run on a scheduler)
+ */
+ void enableMaintenanceRunner(boolean theToEnable);
+
+ /**
+ * Disables the workchunk message handler
+ * so that we do not actually send messages to the queue;
+ * useful if mocking state transitions and we don't want to test
+ * dequeuing.
+ * Returns a latch that will fire each time a message is sent.
+ */
+ PointcutLatch disableWorkChunkMessageHandler();
+
+ /**
+ *
+ * @param theSendingLatch the latch sent back from the disableWorkChunkMessageHandler method above
+ * @param theNumberOfTimes the number of invocations to expect
+ */
+ void verifyWorkChunkMessageHandlerCalled(PointcutLatch theSendingLatch, int theNumberOfTimes) throws InterruptedException;
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkCommon.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkCommon.java
new file mode 100644
index 00000000000..0e80c49288f
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkCommon.java
@@ -0,0 +1,23 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+import ca.uhn.fhir.batch2.model.JobInstance;
+import ca.uhn.fhir.batch2.model.StatusEnum;
+
+public interface IWorkChunkCommon extends WorkChunkTestConstants {
+
+ /**
+ * Returns the concrete class that is implementing this stuff.
+ * Used primarily for structure
+ */
+ ITestFixture getTestManager();
+
+ default JobInstance createInstance() {
+ JobInstance instance = new JobInstance();
+ instance.setJobDefinitionId(JOB_DEFINITION_ID);
+ instance.setStatus(StatusEnum.QUEUED);
+ instance.setJobDefinitionVersion(JOB_DEF_VER);
+ instance.setParameters(CHUNK_DATA);
+ instance.setReport("TEST");
+ return instance;
+ }
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkErrorActionsTests.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkErrorActionsTests.java
new file mode 100644
index 00000000000..98b99350bb5
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkErrorActionsTests.java
@@ -0,0 +1,108 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+import ca.uhn.fhir.batch2.model.WorkChunk;
+import ca.uhn.fhir.batch2.model.WorkChunkCompletionEvent;
+import ca.uhn.fhir.batch2.model.WorkChunkErrorEvent;
+import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public interface IWorkChunkErrorActionsTests extends IWorkChunkCommon, WorkChunkTestConstants {
+
+
+ /**
+ * The consumer will retry after a retryable error is thrown
+ */
+ @Test
+ default void errorRetry_errorToInProgress() {
+ String jobId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId);
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE));
+
+ // when consumer restarts chunk
+ WorkChunk chunk = getTestManager().getSvc().onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new);
+
+ // then
+ assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
+
+ // verify the db state, error message, and error count
+ var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.IN_PROGRESS, workChunkEntity.getStatus());
+ assertEquals(FIRST_ERROR_MESSAGE, workChunkEntity.getErrorMessage(), "Original error message kept");
+ assertEquals(1, workChunkEntity.getErrorCount(), "error count kept");
+ }
+
+ @Test
+ default void errorRetry_repeatError_increasesErrorCount() {
+ String jobId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId);
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE));
+
+ // setup - the consumer is re-trying, and marks it IN_PROGRESS
+ getTestManager().getSvc().onWorkChunkDequeue(myChunkId);
+
+
+ // when another error happens
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B));
+
+ // verify the state, new message, and error count
+ var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.ERRORED, workChunkEntity.getStatus());
+ assertEquals(ERROR_MESSAGE_B, workChunkEntity.getErrorMessage(), "new error message");
+ assertEquals(2, workChunkEntity.getErrorCount(), "error count inc");
+ }
+
+ @Test
+ default void errorThenRetryAndComplete_addsErrorCounts() {
+ String jobId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId);
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE));
+
+ // setup - the consumer is re-trying, and marks it IN_PROGRESS
+ getTestManager().getSvc().onWorkChunkDequeue(myChunkId);
+
+ // then it completes ok.
+ getTestManager().getSvc().onWorkChunkCompletion(new WorkChunkCompletionEvent(myChunkId, 3, 1));
+
+ // verify the state, new message, and error count
+ var workChunkEntity = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.COMPLETED, workChunkEntity.getStatus());
+ assertEquals(FIRST_ERROR_MESSAGE, workChunkEntity.getErrorMessage(), "Error message kept.");
+ assertEquals(2, workChunkEntity.getErrorCount(), "error combined with earlier error");
+ }
+
+ @Test
+ default void errorRetry_maxErrors_movesToFailed() {
+ // we start with 1 error already
+ String jobId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createAndDequeueWorkChunk(jobId);
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, FIRST_ERROR_MESSAGE));
+
+ // 2nd try
+ getTestManager().getSvc().onWorkChunkDequeue(myChunkId);
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B));
+ var chunk = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.ERRORED, chunk.getStatus());
+ assertEquals(2, chunk.getErrorCount());
+
+ // 3rd try
+ getTestManager().getSvc().onWorkChunkDequeue(myChunkId);
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_B));
+ chunk = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.ERRORED, chunk.getStatus());
+ assertEquals(3, chunk.getErrorCount());
+
+ // 4th try
+ getTestManager().getSvc().onWorkChunkDequeue(myChunkId);
+ getTestManager().getSvc().onWorkChunkError(new WorkChunkErrorEvent(myChunkId, ERROR_MESSAGE_C));
+ chunk = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.FAILED, chunk.getStatus());
+ assertEquals(4, chunk.getErrorCount());
+ assertThat("Error message contains last error", chunk.getErrorMessage(), containsString(ERROR_MESSAGE_C));
+ assertThat("Error message contains error count and complaint", chunk.getErrorMessage(), containsString("many errors: 4"));
+ }
+
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStateTransitions.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStateTransitions.java
new file mode 100644
index 00000000000..ac9ba26f2c5
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStateTransitions.java
@@ -0,0 +1,32 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+import ca.uhn.fhir.batch2.model.WorkChunk;
+import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum;
+import ca.uhn.test.concurrency.PointcutLatch;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public interface IWorkChunkStateTransitions extends IWorkChunkCommon, WorkChunkTestConstants {
+
+ Logger ourLog = LoggerFactory.getLogger(IWorkChunkStateTransitions.class);
+
+ @Test
+ default void chunkReceived_queuedToInProgress() throws InterruptedException {
+ String jobInstanceId = getTestManager().createAndStoreJobInstance(null);
+ String myChunkId = getTestManager().createChunk(jobInstanceId);
+
+ getTestManager().runMaintenancePass();
+ // the worker has received the chunk, and marks it started.
+ WorkChunk chunk = getTestManager().getSvc().onWorkChunkDequeue(myChunkId).orElseThrow(IllegalArgumentException::new);
+
+ assertEquals(WorkChunkStatusEnum.IN_PROGRESS, chunk.getStatus());
+ assertEquals(CHUNK_DATA, chunk.getData());
+
+ // verify the db was updated too
+ WorkChunk fetchedWorkChunk = getTestManager().freshFetchWorkChunk(myChunkId);
+ assertEquals(WorkChunkStatusEnum.IN_PROGRESS, fetchedWorkChunk.getStatus());
+ }
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStorageTests.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStorageTests.java
new file mode 100644
index 00000000000..c12de0420ce
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/IWorkChunkStorageTests.java
@@ -0,0 +1,23 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+import ca.uhn.fhir.batch2.model.JobInstance;
+import ca.uhn.fhir.batch2.model.WorkChunk;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public interface IWorkChunkStorageTests extends IWorkChunkCommon, WorkChunkTestConstants {
+
+ @Test
+ default void testStoreAndFetchWorkChunk_NoData() {
+ JobInstance instance = createInstance();
+ String instanceId = getTestManager().getSvc().storeNewInstance(instance);
+
+ String id = getTestManager().storeWorkChunk(JOB_DEFINITION_ID, TARGET_STEP_ID, instanceId, 0, null);
+
+ getTestManager().runInTransaction(() -> {
+ WorkChunk chunk = getTestManager().freshFetchWorkChunk(id);
+ assertNull(chunk.getData());
+ });
+ }
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/WorkChunkTestConstants.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/WorkChunkTestConstants.java
new file mode 100644
index 00000000000..9f5df3120ff
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/WorkChunkTestConstants.java
@@ -0,0 +1,18 @@
+package ca.uhn.hapi.fhir.batch2.test;
+
+public interface WorkChunkTestConstants {
+ public static final String JOB_DEFINITION_ID = "definition-id";
+ // we use a separate id for gated jobs because these job definitions might not
+ // be cleaned up after any given test run
+ String GATED_JOB_DEFINITION_ID = "gated_job_def_id";
+ public static final String TARGET_STEP_ID = "step-id";
+ public static final String DEF_CHUNK_ID = "definition-chunkId";
+ public static final int JOB_DEF_VER = 1;
+ public static final int SEQUENCE_NUMBER = 1;
+ public static final String CHUNK_DATA = "{\"key\":\"value\"}";
+ public static final String ERROR_MESSAGE_A = "This is an error message: A";
+ public static final String ERROR_MESSAGE_B = "This is a different error message: B";
+ public static final String ERROR_MESSAGE_C = "This is a different error message: C";
+
+ String FIRST_ERROR_MESSAGE = ERROR_MESSAGE_A;
+}
diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/configs/SpyOverrideConfig.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/configs/SpyOverrideConfig.java
new file mode 100644
index 00000000000..46331d1c219
--- /dev/null
+++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/configs/SpyOverrideConfig.java
@@ -0,0 +1,27 @@
+package ca.uhn.hapi.fhir.batch2.test.configs;
+
+import ca.uhn.fhir.batch2.channel.BatchJobSender;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import static org.mockito.Mockito.spy;
+
+/**
+ * Provides spying overrides of beans we want to spy on.
+ *
+ * We spy the BatchJobSender so we can test state transitions without
+ * actually sending messages onto the queue
+ */
+@Configuration
+public class SpyOverrideConfig {
+ @Autowired
+ BatchJobSender myRealJobSender;
+
+ @Primary
+ @Bean
+ public BatchJobSender batchJobSenderSpy() {
+ return spy(myRealJobSender);
+ }
+}
diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml
index 2d86204e222..7890ad1bfa3 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobMaintenanceService.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobMaintenanceService.java
index 4b3033a3643..6d4a81ed63b 100644
--- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobMaintenanceService.java
+++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobMaintenanceService.java
@@ -36,4 +36,11 @@ public interface IJobMaintenanceService {
*/
@VisibleForTesting
void forceMaintenancePass();
+
+ /**
+ * This is only to be called in a testing environment
+ * to ensure state changes are controlled.
+ */
+ @VisibleForTesting
+ void enableMaintenancePass(boolean thetoEnable);
}
diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java
index 099d0a86f43..cf2ec48aafe 100644
--- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java
+++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java
@@ -97,6 +97,8 @@ public class JobMaintenanceServiceImpl implements IJobMaintenanceService, IHasSc
private Runnable myMaintenanceJobFinishedCallback = () -> {};
private final IReductionStepExecutorService myReductionStepExecutorService;
+ private boolean myEnabledBool = true;
+
/**
* Constructor
*/
@@ -196,8 +198,17 @@ public class JobMaintenanceServiceImpl implements IJobMaintenanceService, IHasSc
doMaintenancePass();
}
+ @Override
+ public void enableMaintenancePass(boolean theToEnable) {
+ myEnabledBool = theToEnable;
+ }
+
@Override
public void runMaintenancePass() {
+ if (!myEnabledBool) {
+ ourLog.error("Maintenance job is disabled! This will affect all batch2 jobs!");
+ }
+
if (!myRunMaintenanceSemaphore.tryAcquire()) {
ourLog.debug("Another maintenance pass is already in progress. Ignoring request.");
return;
diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml
index deec953b4d9..30a4b7d9731 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml
index aaaffd4dfe4..21684ec422d 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 97697fbeceb..ba4f65c01e5 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml
index 96682b6343b..7090925bf5f 100644
--- a/hapi-fhir-storage/pom.xml
+++ b/hapi-fhir-storage/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml
index 541a22ad700..146924739b1 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml
index 0dab5394bcd..76dadd587ec 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml
index fc3ad9f5c7f..cfddebcbaae 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 04e09044793..3ec00da7fc7 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml
index b4150ec9c38..55a7aeba956 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml
index d852ad84473..73801c099d6 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml
index 1c88c4f4869..67862c00146 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml
index ff040fa535c..eb4c71307da 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml
index 0081f27ff59..02489770f91 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml
index be3911ce0ac..22142383e08 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 ab34e8d8bcc..27b3b119848 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 522ac2b450c..a33191a05ec 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 bb7ed613bb1..44b6a5f1805 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 fb7965abff1..d58235a44b1 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 11e616c37cd..3bde201665c 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml
index bd6a099c68c..b1432477175 100644
--- a/hapi-fhir-validation/pom.xml
+++ b/hapi-fhir-validation/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml
index 4fb7cf0d88e..368e8aaf6a9 100644
--- a/hapi-tinder-plugin/pom.xml
+++ b/hapi-tinder-plugin/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml
index 5f84df6f169..99a2b4d97a8 100644
--- a/hapi-tinder-test/pom.xml
+++ b/hapi-tinder-test/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../pom.xml
diff --git a/pom.xml b/pom.xml
index a3004385ff1..2ce4f6d190a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
ca.uhn.hapi.fhir
hapi-fhir
pom
- 7.1.7-SNAPSHOT
+ 7.1.8-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 5f119fcd90b..890e8f28ed4 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 4250751cced..83b846622ad 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
- 7.1.7-SNAPSHOT
+ 7.1.8-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 0fd0f7c3774..a4d48372ff5 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
- 7.1.7-SNAPSHOT
+ 7.1.8-SNAPSHOT
../../pom.xml