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