diff --git a/addons/common/flyway/src/main/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoader.java b/addons/common/flyway/src/main/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoader.java index 71665bc63d8..986d9eada67 100644 --- a/addons/common/flyway/src/main/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoader.java +++ b/addons/common/flyway/src/main/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoader.java @@ -19,7 +19,6 @@ package org.kie.flyway.initializer.impl; -import java.io.File; import java.io.InputStream; import java.net.URL; import java.util.*; @@ -33,9 +32,7 @@ public class DefaultKieModuleFlywayConfigLoader implements KieModuleFlywayConfigLoader { - public static String KIE_FLYWAY_DESCRIPTOR_FILE_NAME = "kie-flyway.properties"; - - public static String KIE_FLYWAY_DESCRIPTOR_FILE_LOCATION = "META-INF" + File.separator + KIE_FLYWAY_DESCRIPTOR_FILE_NAME; + public static String KIE_FLYWAY_DESCRIPTOR_FILE_LOCATION = "META-INF/kie-flyway.properties"; public static final String MODULE_PREFIX = "module."; public static final String MODULE_NAME_KEY = MODULE_PREFIX + "name"; diff --git a/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayNamedModule.java b/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayNamedModule.java index e3ebe1b6a06..3b4d0644458 100644 --- a/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayNamedModule.java +++ b/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayNamedModule.java @@ -19,7 +19,21 @@ package org.kie.flyway.integration; -public interface KieFlywayNamedModule { +public class KieFlywayNamedModule { - boolean isEnabled(); + private final String name; + private final boolean enabled; + + public KieFlywayNamedModule(String name, boolean enabled) { + this.name = name; + this.enabled = enabled; + } + + public String getName() { + return name; + } + + public boolean isEnabled() { + return enabled; + } } diff --git a/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayRunner.java b/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayRunner.java index 47d55a96e6b..ddc68b35d0b 100644 --- a/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayRunner.java +++ b/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayRunner.java @@ -20,7 +20,6 @@ package org.kie.flyway.integration; import java.util.Collection; -import java.util.Map; import java.util.Objects; import javax.sql.DataSource; @@ -34,18 +33,18 @@ public class KieFlywayRunner { private static final Logger LOGGER = LoggerFactory.getLogger(KieFlywayRunner.class); private final ClassLoader classLoader; - private final KieFlywayConfiguration configuration; + private final KieFlywayRunnerConfiguration configuration; - private KieFlywayRunner(KieFlywayConfiguration configuration) { + private KieFlywayRunner(KieFlywayRunnerConfiguration configuration) { this(configuration, Thread.currentThread().getContextClassLoader()); } - protected KieFlywayRunner(KieFlywayConfiguration configuration, ClassLoader classLoader) { + protected KieFlywayRunner(KieFlywayRunnerConfiguration configuration, ClassLoader classLoader) { this.configuration = configuration; this.classLoader = classLoader; } - public static KieFlywayRunner get(KieFlywayConfiguration configuration) { + public static KieFlywayRunner get(KieFlywayRunnerConfiguration configuration) { return new KieFlywayRunner(configuration); } @@ -60,9 +59,9 @@ public void runFlyway(DataSource dataSource) { assertValue(dataSource, "Kie Flyway: Cannot run Kie Flyway migration default datasource is null"); Collection excludedModules = configuration.getModules() - .entrySet() - .stream().filter(entry -> !entry.getValue().isEnabled()) - .map(Map.Entry::getKey) + .stream() + .filter(module -> !module.isEnabled()) + .map(KieFlywayNamedModule::getName) .toList(); KieFlywayInitializer.builder() diff --git a/addons/common/flyway/src/test/java/org/kie/flyway/integration/TestKieFlywayConfiguration.java b/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayRunnerConfiguration.java similarity index 67% rename from addons/common/flyway/src/test/java/org/kie/flyway/integration/TestKieFlywayConfiguration.java rename to addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayRunnerConfiguration.java index 1d20272a081..81ebbee4b99 100644 --- a/addons/common/flyway/src/test/java/org/kie/flyway/integration/TestKieFlywayConfiguration.java +++ b/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayRunnerConfiguration.java @@ -19,29 +19,23 @@ package org.kie.flyway.integration; -import java.util.Map; +import java.util.Collection; -public class TestKieFlywayConfiguration implements KieFlywayConfiguration { +public class KieFlywayRunnerConfiguration { - private boolean enabled; - private Map modules; + private final boolean enabled; + private final Collection modules; - public TestKieFlywayConfiguration(boolean enabled, Map modules) { + public KieFlywayRunnerConfiguration(boolean enabled, Collection modules) { this.enabled = enabled; this.modules = modules; } - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - @Override public boolean isEnabled() { return enabled; } - @Override - public Map getModules() { + public Collection getModules() { return modules; } } diff --git a/addons/common/flyway/src/test/java/org/kie/flyway/initializer/KieFlywayInitializerTest.java b/addons/common/flyway/src/test/java/org/kie/flyway/initializer/KieFlywayInitializerTest.java index 425c3b32c9b..16ca985ba04 100644 --- a/addons/common/flyway/src/test/java/org/kie/flyway/initializer/KieFlywayInitializerTest.java +++ b/addons/common/flyway/src/test/java/org/kie/flyway/initializer/KieFlywayInitializerTest.java @@ -22,7 +22,6 @@ import java.util.*; import java.util.stream.Stream; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -37,6 +36,7 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.kie.flyway.test.models.TestModels.*; @Testcontainers @@ -71,17 +71,17 @@ public void init() { @ParameterizedTest @MethodSource("getDataSources") public void testTestKieFlywayInitializerBuilderValidations(TestDataSource dataSource) { - Assertions.assertThatThrownBy(() -> KieFlywayInitializer.builder() + assertThatThrownBy(() -> KieFlywayInitializer.builder() .build()).isInstanceOf(KieFlywayException.class) - .hasMessage("Cannot create KieFlywayInitializer migration, dataSource is null."); + .hasMessage("Cannot create KieFlywayInitializer migration, dataSource is null."); classLoader.addKieFlywayModule("initializers/kie-flyway.no.locations.properties"); - Assertions.assertThatThrownBy(() -> KieFlywayInitializer.builder() + assertThatThrownBy(() -> KieFlywayInitializer.builder() .withDatasource(dataSource.getDataSource()) .withClassLoader(classLoader).build().migrate()) - .isInstanceOf(KieFlywayException.class) - .hasMessageContaining("Cannot run Flyway migration for module `no-locations`, cannot find SQL Script locations for db"); + .isInstanceOf(KieFlywayException.class) + .hasMessageContaining("Cannot run Flyway migration for module `no-locations`, cannot find SQL Script locations for db"); } @ParameterizedTest @@ -92,7 +92,7 @@ public void testKieFlywayInitializerValidations(TestDataSource dataSource) { classLoader.addKieFlywayModule("initializers/kie-flyway.duplicated2.properties"); classLoader.addKieFlywayModule("initializers/kie-flyway.duplicated2.properties"); - Assertions.assertThatThrownBy(() -> { + assertThatThrownBy(() -> { KieFlywayInitializer.builder() .withDatasource(dataSource.getDataSource()) .withClassLoader(classLoader) diff --git a/addons/common/flyway/src/test/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoaderTest.java b/addons/common/flyway/src/test/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoaderTest.java index 139f48c649c..1fc30bac499 100644 --- a/addons/common/flyway/src/test/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoaderTest.java +++ b/addons/common/flyway/src/test/java/org/kie/flyway/initializer/impl/DefaultKieModuleFlywayConfigLoaderTest.java @@ -21,13 +21,15 @@ import java.util.Collection; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.kie.flyway.KieFlywayException; import org.kie.flyway.model.KieFlywayModuleConfig; import org.kie.flyway.test.utils.TestClassLoader; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + public class DefaultKieModuleFlywayConfigLoaderTest { private static final String H2_LOCATIONS = "classpath:kie-flyway/db/test/h2"; @@ -48,10 +50,10 @@ public void testDefaultLoading() { Collection configs = flywayConfigLoader.loadModuleConfigs(); - Assertions.assertThat(configs) + assertThat(configs) .hasSize(1); - Assertions.assertThat(configs.iterator().next()) + assertThat(configs.iterator().next()) .isNotNull() .hasFieldOrPropertyWithValue("module", "test") .returns(H2_LOCATIONS, kieFlywayModuleConfig -> kieFlywayModuleConfig.getDBScriptLocations("h2")[0]) @@ -63,7 +65,7 @@ public void testDefaultLoading() { public void testEmptyConfigFile() { testClassLoader.addKieFlywayModule("initializers/kie-flyway.empty.properties"); - Assertions.assertThatThrownBy(() -> flywayConfigLoader.loadModuleConfigs()) + assertThatThrownBy(() -> flywayConfigLoader.loadModuleConfigs()) .isInstanceOf(KieFlywayException.class) .hasMessage("Could not load ModuleFlywayConfig") .cause() @@ -75,7 +77,7 @@ public void testEmptyConfigFile() { public void testWrongLocationsFormat() { testClassLoader.addKieFlywayModule("initializers/kie-flyway.wrong.format.properties"); - Assertions.assertThatThrownBy(() -> flywayConfigLoader.loadModuleConfigs()) + assertThatThrownBy(() -> flywayConfigLoader.loadModuleConfigs()) .isInstanceOf(KieFlywayException.class) .hasMessage("Could not load ModuleFlywayConfig") .cause() @@ -87,7 +89,7 @@ public void testWrongLocationsFormat() { public void testWrongResourceFile() { testClassLoader.addKieFlywayModule("wrong content"); - Assertions.assertThatThrownBy(() -> flywayConfigLoader.loadModuleConfigs()) + assertThatThrownBy(() -> flywayConfigLoader.loadModuleConfigs()) .isInstanceOf(KieFlywayException.class) .hasMessage("Could not load ModuleFlywayConfig"); } diff --git a/addons/common/flyway/src/test/java/org/kie/flyway/integration/KieFlywayRunnerTest.java b/addons/common/flyway/src/test/java/org/kie/flyway/integration/KieFlywayRunnerTest.java index bd510d9994f..ae0109b6860 100644 --- a/addons/common/flyway/src/test/java/org/kie/flyway/integration/KieFlywayRunnerTest.java +++ b/addons/common/flyway/src/test/java/org/kie/flyway/integration/KieFlywayRunnerTest.java @@ -19,11 +19,11 @@ package org.kie.flyway.integration; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; import javax.sql.DataSource; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.*; import org.kie.flyway.KieFlywayException; import org.kie.flyway.test.AbstractKieFlywayTest; @@ -31,6 +31,7 @@ import org.kie.flyway.test.dataSources.TestDataSource; import org.kie.flyway.test.utils.TestClassLoader; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.kie.flyway.test.models.TestModels.*; import static org.mockito.Mockito.mock; @@ -44,27 +45,27 @@ public class KieFlywayRunnerTest extends AbstractKieFlywayTest { } private TestClassLoader testClassLoader; - private TestKieFlywayConfiguration testConfiguration; @BeforeEach public void init() { testClassLoader = new TestClassLoader(this.getClass().getClassLoader()); - testConfiguration = new TestKieFlywayConfiguration(true, new HashMap<>()); } @Test public void testValidations() { - Assertions.assertThatThrownBy(() -> KieFlywayRunner.get(null).runFlyway(null)) + assertThatThrownBy(() -> KieFlywayRunner.get(null).runFlyway(null)) .isInstanceOf(KieFlywayException.class) .hasMessage("Kie Flyway: Cannot run Kie Flyway migration configuration is null."); - Assertions.assertThatThrownBy(() -> KieFlywayRunner.get(testConfiguration).runFlyway(null)).isInstanceOf(KieFlywayException.class) + KieFlywayRunnerConfiguration config = new KieFlywayRunnerConfiguration(true, new ArrayList<>()); + + assertThatThrownBy(() -> KieFlywayRunner.get(config).runFlyway(null)).isInstanceOf(KieFlywayException.class) .hasMessage("Kie Flyway: Cannot run Kie Flyway migration default datasource is null"); // Mocking DataSource to make sure we cannot resolve dbType. DataSource mockedDS = mock(DataSource.class); - Assertions.assertThatThrownBy(() -> KieFlywayRunner.get(testConfiguration).runFlyway(mockedDS)) + assertThatThrownBy(() -> KieFlywayRunner.get(config).runFlyway(mockedDS)) .isInstanceOf(KieFlywayException.class) .hasMessage("Kie Flyway: Couldn't extract database product name from datasource."); } @@ -75,9 +76,9 @@ public void testFlywayMigrationsWithDisabledConfig() { testClassLoader.addKieFlywayModule("initializers/kie-flyway.customers.properties"); testClassLoader.addKieFlywayModule("initializers/kie-flyway.guitars.properties"); - testConfiguration.setEnabled(false); + KieFlywayRunnerConfiguration config = new KieFlywayRunnerConfiguration(false, new ArrayList<>()); - TestKieFlywayRunner.get(testConfiguration, testClassLoader) + TestKieFlywayRunner.get(config, testClassLoader) .runFlyway(TEST_DATA_SOURCE.getDataSource()); verifyTableDoesntExist("customers", TEST_DATA_SOURCE); @@ -89,13 +90,12 @@ public void testFlywayMigrationsWithDisabledConfig() { @Test @Order(1) public void testFlywayMigrationsWithExclusions() { - - testConfiguration.getModules().put("guitars", new TestKieFlywayNamedModule(false)); - testClassLoader.addKieFlywayModule("initializers/kie-flyway.customers.properties"); testClassLoader.addKieFlywayModule("initializers/kie-flyway.guitars.properties"); - TestKieFlywayRunner.get(testConfiguration, testClassLoader) + KieFlywayRunnerConfiguration config = new KieFlywayRunnerConfiguration(true, List.of(new KieFlywayNamedModule("guitars", false))); + + TestKieFlywayRunner.get(config, testClassLoader) .runFlyway(TEST_DATA_SOURCE.getDataSource()); validateKieFlywayIndex("customers", EXPECTED_CUSTOMERS_MIGRATIONS.stream().limit(3).toList(), TEST_DATA_SOURCE); @@ -113,7 +113,9 @@ public void testFlywayMigrationsUpgrade() { testClassLoader.addKieFlywayModule("initializers/kie-flyway.customers2.properties"); testClassLoader.addKieFlywayModule("initializers/kie-flyway.guitars.properties"); - TestKieFlywayRunner.get(testConfiguration, testClassLoader) + KieFlywayRunnerConfiguration config = new KieFlywayRunnerConfiguration(true, new ArrayList<>()); + + TestKieFlywayRunner.get(config, testClassLoader) .runFlyway(TEST_DATA_SOURCE.getDataSource()); validateKieFlywayIndex("customers", EXPECTED_CUSTOMERS_MIGRATIONS, TEST_DATA_SOURCE); @@ -130,11 +132,11 @@ public static void shutdown() { public static class TestKieFlywayRunner extends KieFlywayRunner { - protected TestKieFlywayRunner(KieFlywayConfiguration configuration, ClassLoader classLoader) { + protected TestKieFlywayRunner(KieFlywayRunnerConfiguration configuration, ClassLoader classLoader) { super(configuration, classLoader); } - public static KieFlywayRunner get(TestKieFlywayConfiguration configuration, ClassLoader classLoader) { + public static KieFlywayRunner get(KieFlywayRunnerConfiguration configuration, ClassLoader classLoader) { return new TestKieFlywayRunner(configuration, classLoader); } } diff --git a/addons/common/flyway/src/test/java/org/kie/flyway/test/AbstractKieFlywayTest.java b/addons/common/flyway/src/test/java/org/kie/flyway/test/AbstractKieFlywayTest.java index 3a6ff7fa3ff..1ee734a6839 100644 --- a/addons/common/flyway/src/test/java/org/kie/flyway/test/AbstractKieFlywayTest.java +++ b/addons/common/flyway/src/test/java/org/kie/flyway/test/AbstractKieFlywayTest.java @@ -24,13 +24,13 @@ import java.sql.ResultSet; import java.util.Collection; -import org.assertj.core.api.Assertions; import org.kie.flyway.initializer.KieFlywayInitializerTest; import org.kie.flyway.test.dataSources.TestDataSource; import org.kie.flyway.test.models.Customer; import org.kie.flyway.test.models.Guitar; import org.kie.flyway.test.models.KieFlywayMigration; +import static org.assertj.core.api.Assertions.assertThat; import static org.kie.flyway.test.models.TestModels.EXPECTED_GUITARS; public abstract class AbstractKieFlywayTest { @@ -48,15 +48,15 @@ private void validateFlywayMigration(final String moduleName, final KieFlywayMig PreparedStatement stmt = con.prepareStatement(KieFlywayInitializerTest.MODULE_MIGRATIONS_QUERY_TEMPLATE.formatted(moduleName));) { stmt.setString(1, migration.version()); try (ResultSet rs = stmt.executeQuery()) { - Assertions.assertThat(rs.next()) + assertThat(rs.next()) .isTrue(); - Assertions.assertThat(rs.getString("version")) + assertThat(rs.getString("version")) .isEqualTo(migration.version()); - Assertions.assertThat(rs.getString("description")) + assertThat(rs.getString("description")) .isEqualTo(migration.description().formatted(dataSource.getDbType())); - Assertions.assertThat(rs.getBoolean("success")) + assertThat(rs.getBoolean("success")) .isEqualTo(true); - Assertions.assertThat(rs.next()) + assertThat(rs.next()) .isFalse(); } } catch (Exception ex) { @@ -70,18 +70,18 @@ protected void validateCustomersData(Collection expectedCustomers, Tes ResultSet rs = stmt.executeQuery()) { for (Customer customer : expectedCustomers) { - Assertions.assertThat(rs.next()) + assertThat(rs.next()) .isTrue(); - Assertions.assertThat(rs.getInt("id")) + assertThat(rs.getInt("id")) .isEqualTo(customer.id()); - Assertions.assertThat(rs.getString("name")) + assertThat(rs.getString("name")) .isEqualTo(customer.name()); - Assertions.assertThat(rs.getString("last_name")) + assertThat(rs.getString("last_name")) .isEqualTo(customer.lastName()); - Assertions.assertThat(rs.getString("email")) + assertThat(rs.getString("email")) .isEqualTo(customer.email()); } - Assertions.assertThat(rs.next()) + assertThat(rs.next()) .isFalse(); } catch (Exception ex) { throw new RuntimeException(ex); @@ -94,19 +94,19 @@ protected void validateGuitarsData(TestDataSource dataSource) { ResultSet rs = stmt.executeQuery()) { for (Guitar guitar : EXPECTED_GUITARS) { - Assertions.assertThat(rs.next()) + assertThat(rs.next()) .isTrue(); - Assertions.assertThat(rs.getInt("id")) + assertThat(rs.getInt("id")) .isEqualTo(guitar.id()); - Assertions.assertThat(rs.getString("brand")) + assertThat(rs.getString("brand")) .isEqualTo(guitar.brand()); - Assertions.assertThat(rs.getString("model")) + assertThat(rs.getString("model")) .isEqualTo(guitar.model()); - Assertions.assertThat(rs.getInt("rating")) + assertThat(rs.getInt("rating")) .isEqualTo(guitar.rating()); } - Assertions.assertThat(rs.next()) + assertThat(rs.next()) .isFalse(); } catch (Exception ex) { throw new RuntimeException(ex); @@ -118,8 +118,8 @@ protected void verifyTableDoesntExist(String tableName, TestDataSource dataSourc PreparedStatement stmt = con.prepareStatement(QUERY_QUERY_TABLE_EXISTS);) { stmt.setString(1, tableName); try (ResultSet rs = stmt.executeQuery()) { - Assertions.assertThat(rs.next()).isTrue(); - Assertions.assertThat(rs.getInt("count")).isEqualTo(0); + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt("count")).isEqualTo(0); } } catch (Exception ex) { throw new RuntimeException(ex); diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapper.java index 210553ed853..96aee9ee701 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapper.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapper.java @@ -33,13 +33,14 @@ import static java.util.stream.Collectors.toCollection; -public class AttachmentsEntityMapper { +public class AttachmentsEntityMapper implements EntityMapper { private final AttachmentRepository repository; public AttachmentsEntityMapper(AttachmentRepository repository) { this.repository = repository; } + @Override public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity) { Collection toRemove = userTaskInstanceEntity.getAttachments() .stream() @@ -65,6 +66,7 @@ public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInsta }); } + @Override public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { List attachments = userTaskInstanceEntity.getAttachments().stream().map(attachmentEntity -> { diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/CommentsEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/CommentsEntityMapper.java index b854ec3510a..d705ef882e7 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/CommentsEntityMapper.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/CommentsEntityMapper.java @@ -32,7 +32,7 @@ import static java.util.stream.Collectors.toCollection; -public class CommentsEntityMapper { +public class CommentsEntityMapper implements EntityMapper { private final CommentRepository repository; @@ -40,6 +40,7 @@ public CommentsEntityMapper(CommentRepository repository) { this.repository = repository; } + @Override public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity) { Collection toRemove = userTaskInstanceEntity.getComments() .stream() @@ -64,6 +65,7 @@ public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInsta }); } + @Override public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { List comments = userTaskInstanceEntity.getComments().stream().map(commentEntity -> { Comment comment = new Comment(commentEntity.getId(), commentEntity.getUpdatedBy()); diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/EntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/EntityMapper.java new file mode 100644 index 00000000000..d9759d6e1a5 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/EntityMapper.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jbpm.usertask.jpa.mapper; + +import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; +import org.kie.kogito.usertask.UserTaskInstance; + +public interface EntityMapper { + + void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity); + + void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance); + +} \ No newline at end of file diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskDeadlineEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskDeadlineEntityMapper.java new file mode 100644 index 00000000000..7d28bbdbda3 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskDeadlineEntityMapper.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.mapper; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.jbpm.usertask.jpa.mapper.json.utils.JSONUtils; +import org.jbpm.usertask.jpa.model.TaskDeadlineEntity; +import org.jbpm.usertask.jpa.model.TaskDeadlineType; +import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; +import org.jbpm.usertask.jpa.repository.TaskDeadlineRepository; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; +import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Notification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JavaType; + +public class TaskDeadlineEntityMapper implements EntityMapper { + private static final Logger LOG = LoggerFactory.getLogger(TaskDeadlineEntityMapper.class); + + private final TaskDeadlineRepository repository; + + public TaskDeadlineEntityMapper(TaskDeadlineRepository repository) { + this.repository = repository; + } + + @Override + public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity) { + if (userTaskInstanceEntity.getDeadlines() == null) { + userTaskInstanceEntity.setDeadlines(new ArrayList<>()); + } + + List entities = new ArrayList<>(); + List> notStartedNotifications = new ArrayList<>(); + List> notCompletedNotifications = new ArrayList<>(); + Iterator iterator = userTaskInstanceEntity.getDeadlines().iterator(); + while (iterator.hasNext()) { + TaskDeadlineEntity deadlineEntity = iterator.next(); + DeadlineInfo deadline = readNotification(deadlineEntity.getValue()); + switch (deadlineEntity.getType()) { + case NotCompleted: + if (!userTaskInstance.getNotCompletedDeadlines().contains(deadline)) { + entities.add(deadlineEntity); + } else { + notCompletedNotifications.add(deadline); + } + break; + case NotStarted: + if (!userTaskInstance.getNotStartedDeadlines().contains(deadline)) { + entities.add(deadlineEntity); + } else { + notStartedNotifications.add(deadline); + } + break; + } + } + + entities.forEach(e -> { + repository.remove(e); + userTaskInstanceEntity.getDeadlines().remove(e); + }); + entities.clear(); + + for (DeadlineInfo deadline : userTaskInstance.getNotStartedDeadlines()) { + if (!notStartedNotifications.contains(deadline)) { + entities.add(buildEntity(userTaskInstanceEntity, deadline, TaskDeadlineType.NotStarted)); + } + } + for (DeadlineInfo deadline : userTaskInstance.getNotCompletedDeadlines()) { + if (!notCompletedNotifications.contains(deadline)) { + entities.add(buildEntity(userTaskInstanceEntity, deadline, TaskDeadlineType.NotCompleted)); + } + } + entities.forEach(e -> { + userTaskInstanceEntity.getDeadlines().add(e); + }); + } + + private TaskDeadlineEntity buildEntity(UserTaskInstanceEntity userTaskInstanceEntity, DeadlineInfo deadline, TaskDeadlineType type) { + TaskDeadlineEntity entity = new TaskDeadlineEntity(); + entity.setTaskInstance(userTaskInstanceEntity); + entity.setJavaType(DeadlineInfo.class.getName()); + entity.setValue(JSONUtils.valueToString(deadline).getBytes(StandardCharsets.UTF_8)); + entity.setType(type); + return entity; + } + + @Override + public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { + List> notStarted = new ArrayList<>(); + List> notCompleted = new ArrayList<>(); + + for (TaskDeadlineEntity entity : userTaskInstanceEntity.getDeadlines()) { + DeadlineInfo deadline = readNotification(entity.getValue()); + switch (entity.getType()) { + case NotCompleted: + notCompleted.add(deadline); + break; + case NotStarted: + notStarted.add(deadline); + break; + } + } + + ((DefaultUserTaskInstance) userTaskInstance).setNotStartedDeadlines(notStarted); + ((DefaultUserTaskInstance) userTaskInstance).setNotCompletedDeadlines(notCompleted); + } + + private DeadlineInfo readNotification(byte[] value) { + JavaType javaType = JSONUtils.buildJavaType(DeadlineInfo.class, Notification.class); + return (DeadlineInfo) JSONUtils.stringTreeToValue(new String(value), javaType); + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskDeadlineTimerEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskDeadlineTimerEntityMapper.java new file mode 100644 index 00000000000..568cea2e6fd --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskDeadlineTimerEntityMapper.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.mapper; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.jbpm.usertask.jpa.mapper.json.utils.JSONUtils; +import org.jbpm.usertask.jpa.model.TaskDeadlineTimerEntity; +import org.jbpm.usertask.jpa.model.TaskDeadlineType; +import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; +import org.jbpm.usertask.jpa.repository.TaskDeadlineTimerRepository; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; +import org.kie.kogito.usertask.model.Notification; + +public class TaskDeadlineTimerEntityMapper implements EntityMapper { + + private final TaskDeadlineTimerRepository repository; + + public TaskDeadlineTimerEntityMapper(TaskDeadlineTimerRepository repository) { + this.repository = repository; + } + + @Override + public void mapInstanceToEntity(UserTaskInstance instance, UserTaskInstanceEntity userTaskInstanceEntity) { + if (userTaskInstanceEntity.getDeadlineTimers() == null) { + userTaskInstanceEntity.setDeadlineTimers(new ArrayList<>()); + } + DefaultUserTaskInstance userTaskInstance = (DefaultUserTaskInstance) instance; + List notStartedNotifications = new ArrayList<>(); + List notCompletedNotifications = new ArrayList<>(); + + List entities = new ArrayList<>(); + Iterator iterator = userTaskInstanceEntity.getDeadlineTimers().iterator(); + while (iterator.hasNext()) { + TaskDeadlineTimerEntity deadlineEntity = iterator.next(); + switch (deadlineEntity.getType()) { + case NotCompleted: + if (!userTaskInstance.getNotCompletedDeadlinesTimers().keySet().contains(deadlineEntity.getJobId())) { + entities.add(deadlineEntity); + } else { + notCompletedNotifications.add(deadlineEntity.getJobId()); + } + break; + case NotStarted: + if (!userTaskInstance.getNotStartedDeadlinesTimers().keySet().contains(deadlineEntity.getJobId())) { + entities.add(deadlineEntity); + } else { + notStartedNotifications.add(deadlineEntity.getJobId()); + } + break; + } + } + + entities.forEach(e -> { + repository.remove(e); + userTaskInstanceEntity.getDeadlineTimers().remove(e); + }); + entities.clear(); + + for (Map.Entry timer : userTaskInstance.getNotStartedDeadlinesTimers().entrySet()) { + if (!notStartedNotifications.contains(timer.getKey())) { + TaskDeadlineTimerEntity entity = buildEntity(userTaskInstanceEntity, timer.getKey(), timer.getValue(), TaskDeadlineType.NotStarted); + entities.add(entity); + } + } + + for (Map.Entry timer : userTaskInstance.getNotCompletedDeadlinesTimers().entrySet()) { + if (!notCompletedNotifications.contains(timer.getKey())) { + TaskDeadlineTimerEntity entity = buildEntity(userTaskInstanceEntity, timer.getKey(), timer.getValue(), TaskDeadlineType.NotCompleted); + entities.add(entity); + } + } + entities.forEach(userTaskInstanceEntity.getDeadlineTimers()::add); + } + + private TaskDeadlineTimerEntity buildEntity(UserTaskInstanceEntity userTaskInstanceEntity, String jobId, Notification deadline, TaskDeadlineType type) { + TaskDeadlineTimerEntity entity = new TaskDeadlineTimerEntity(); + entity.setTaskInstance(userTaskInstanceEntity); + entity.setJobId(jobId); + entity.setJavaType(Notification.class.getName()); + entity.setValue(JSONUtils.valueToString(deadline).getBytes(StandardCharsets.UTF_8)); + entity.setType(type); + return entity; + } + + @Override + public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { + Map notStarted = new HashMap<>(); + Map notCompleted = new HashMap<>(); + for (TaskDeadlineTimerEntity entity : userTaskInstanceEntity.getDeadlineTimers()) { + Notification notification = (Notification) JSONUtils.stringTreeToValue(new String(entity.getValue()), Notification.class.getName()); + switch (entity.getType()) { + case NotCompleted: + notCompleted.put(entity.getJobId(), notification); + break; + case NotStarted: + notStarted.put(entity.getJobId(), notification); + break; + } + } + + ((DefaultUserTaskInstance) userTaskInstance).setNotStartedDeadlinesTimers(notStarted); + ((DefaultUserTaskInstance) userTaskInstance).setNotCompletedDeadlinesTimers(notCompleted); + } + + private Notification readNotification(byte[] value) { + return (Notification) JSONUtils.stringTreeToValue(new String(value), Notification.class.getName()); + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskInputsEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskInputsEntityMapper.java index 12a6c50f986..a61ffabcddc 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskInputsEntityMapper.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskInputsEntityMapper.java @@ -32,7 +32,7 @@ import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; -public class TaskInputsEntityMapper { +public class TaskInputsEntityMapper implements EntityMapper { private TaskInputRepository repository; @@ -40,6 +40,7 @@ public TaskInputsEntityMapper(TaskInputRepository repository) { this.repository = repository; } + @Override public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity) { Collection toRemove = userTaskInstanceEntity.getInputs() .stream() @@ -66,6 +67,7 @@ public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInsta }); } + @Override public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { Map inputs = new HashMap<>(); userTaskInstanceEntity.getInputs().forEach(taskInputEntity -> { diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskMetadataEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskMetadataEntityMapper.java index 999c7357496..b4af4fc8a9e 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskMetadataEntityMapper.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskMetadataEntityMapper.java @@ -31,7 +31,7 @@ import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; -public class TaskMetadataEntityMapper { +public class TaskMetadataEntityMapper implements EntityMapper { private final TaskMetadataRepository repository; @@ -39,6 +39,7 @@ public TaskMetadataEntityMapper(TaskMetadataRepository repository) { this.repository = repository; } + @Override public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity) { Collection toRemove = userTaskInstanceEntity.getMetadata() .stream() @@ -64,6 +65,7 @@ public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInsta }); } + @Override public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { Map metadata = new HashMap<>(); userTaskInstanceEntity.getMetadata().forEach(metadataEntry -> { diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskOutputsEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskOutputsEntityMapper.java index 1fa7d4ae282..5f93dd53ee9 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskOutputsEntityMapper.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskOutputsEntityMapper.java @@ -32,7 +32,7 @@ import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; -public class TaskOutputsEntityMapper { +public class TaskOutputsEntityMapper implements EntityMapper { private final TaskOutputRepository repository; @@ -40,6 +40,7 @@ public TaskOutputsEntityMapper(TaskOutputRepository repository) { this.repository = repository; } + @Override public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity) { Collection toRemove = userTaskInstanceEntity.getOutputs() .stream() @@ -66,6 +67,7 @@ public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInsta }); } + @Override public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { Map outputs = new HashMap<>(); userTaskInstanceEntity.getOutputs().forEach(taskOutputEntity -> { diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskReassignmentEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskReassignmentEntityMapper.java new file mode 100644 index 00000000000..84a4f78308f --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskReassignmentEntityMapper.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.mapper; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.jbpm.usertask.jpa.mapper.json.utils.JSONUtils; +import org.jbpm.usertask.jpa.model.TaskReassignmentEntity; +import org.jbpm.usertask.jpa.model.TaskReassignmentType; +import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; +import org.jbpm.usertask.jpa.repository.TaskReassignmentRepository; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; +import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Reassignment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JavaType; + +public class TaskReassignmentEntityMapper implements EntityMapper { + + private static final Logger LOG = LoggerFactory.getLogger(TaskReassignmentEntityMapper.class); + + private final TaskReassignmentRepository repository; + + public TaskReassignmentEntityMapper(TaskReassignmentRepository repository) { + this.repository = repository; + } + + @Override + public void mapInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity userTaskInstanceEntity) { + if (userTaskInstanceEntity.getReassignments() == null) { + userTaskInstanceEntity.setReassignments(new ArrayList<>()); + } + List entities = new ArrayList<>(); + List> notStartedReassignments = new ArrayList<>(); + List> notCompletedReassignments = new ArrayList<>(); + Iterator iterator = userTaskInstanceEntity.getReassignments().iterator(); + while (iterator.hasNext()) { + TaskReassignmentEntity reassignmentEntity = iterator.next(); + DeadlineInfo deadline = readNotification(reassignmentEntity.getValue()); + switch (reassignmentEntity.getType()) { + case NotCompleted: + if (!userTaskInstance.getNotCompletedReassignments().contains(deadline)) { + entities.add(reassignmentEntity); + } else { + notCompletedReassignments.add(deadline); + } + break; + case NotStarted: + if (!userTaskInstance.getNotStartedReassignments().contains(deadline)) { + entities.add(reassignmentEntity); + } else { + notStartedReassignments.add(deadline); + } + break; + } + } + + entities.forEach(e -> { + repository.remove(e); + userTaskInstanceEntity.getReassignments().remove(e); + }); + entities.clear(); + + for (DeadlineInfo reassignment : userTaskInstance.getNotStartedReassignments()) { + if (!notStartedReassignments.contains(reassignment)) { + entities.add(buildEntity(userTaskInstanceEntity, reassignment, TaskReassignmentType.NotStarted)); + } + } + + for (DeadlineInfo reassignment : userTaskInstance.getNotCompletedReassignments()) { + if (!notCompletedReassignments.contains(reassignment)) { + entities.add(buildEntity(userTaskInstanceEntity, reassignment, TaskReassignmentType.NotCompleted)); + } + } + entities.forEach(e -> { + userTaskInstanceEntity.getReassignments().add(e); + }); + } + + private TaskReassignmentEntity buildEntity(UserTaskInstanceEntity userTaskInstanceEntity, DeadlineInfo deadline, TaskReassignmentType type) { + TaskReassignmentEntity entity = new TaskReassignmentEntity(); + entity.setTaskInstance(userTaskInstanceEntity); + entity.setJavaType(DeadlineInfo.class.getName()); + entity.setValue(JSONUtils.valueToString(deadline).getBytes(StandardCharsets.UTF_8)); + entity.setType(type); + return entity; + } + + private DeadlineInfo readNotification(byte[] value) { + JavaType javaType = JSONUtils.buildJavaType(DeadlineInfo.class, Reassignment.class); + return (DeadlineInfo) JSONUtils.stringTreeToValue(new String(value), javaType); + } + + @Override + public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { + List> notStarted = new ArrayList<>(); + List> notCompleted = new ArrayList<>(); + JavaType javaType = JSONUtils.buildJavaType(DeadlineInfo.class, Reassignment.class); + for (TaskReassignmentEntity entity : userTaskInstanceEntity.getReassignments()) { + DeadlineInfo deadline = (DeadlineInfo) JSONUtils.stringTreeToValue(new String(entity.getValue()), javaType); + switch (entity.getType()) { + case NotCompleted: + notCompleted.add(deadline); + break; + case NotStarted: + notStarted.add(deadline); + break; + } + } + + ((DefaultUserTaskInstance) userTaskInstance).setNotStartedReassignments(notStarted); + ((DefaultUserTaskInstance) userTaskInstance).setNotCompletedReassignments(notCompleted); + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskReassignmentTimerEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskReassignmentTimerEntityMapper.java new file mode 100644 index 00000000000..80366a07be7 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/TaskReassignmentTimerEntityMapper.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.mapper; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.jbpm.usertask.jpa.mapper.json.utils.JSONUtils; +import org.jbpm.usertask.jpa.model.TaskReassignmentTimerEntity; +import org.jbpm.usertask.jpa.model.TaskReassignmentType; +import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; +import org.jbpm.usertask.jpa.repository.TaskReassignmentTimerRepository; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; +import org.kie.kogito.usertask.model.Reassignment; + +public class TaskReassignmentTimerEntityMapper implements EntityMapper { + + private final TaskReassignmentTimerRepository repository; + + public TaskReassignmentTimerEntityMapper(TaskReassignmentTimerRepository repository) { + this.repository = repository; + } + + @Override + public void mapInstanceToEntity(UserTaskInstance instance, UserTaskInstanceEntity userTaskInstanceEntity) { + if (userTaskInstanceEntity.getReassignmentTimers() == null) { + userTaskInstanceEntity.setReassignmentTimers(new ArrayList<>()); + } + DefaultUserTaskInstance userTaskInstance = (DefaultUserTaskInstance) instance; + + List entities = new ArrayList<>(); + List notStartedReassignments = new ArrayList<>(); + List notCompletedReassignments = new ArrayList<>(); + + Iterator iterator = userTaskInstanceEntity.getReassignmentTimers().iterator(); + while (iterator.hasNext()) { + TaskReassignmentTimerEntity deadlineEntity = iterator.next(); + switch (deadlineEntity.getType()) { + case NotCompleted: + if (!userTaskInstance.getNotCompletedReassignmentsTimers().keySet().contains(deadlineEntity.getJobId())) { + entities.add(deadlineEntity); + } else { + notCompletedReassignments.add(deadlineEntity.getJobId()); + } + break; + case NotStarted: + if (!userTaskInstance.getNotStartedReassignmentsTimers().keySet().contains(deadlineEntity.getJobId())) { + entities.add(deadlineEntity); + } else { + notStartedReassignments.add(deadlineEntity.getJobId()); + } + break; + } + } + + entities.forEach(e -> { + repository.remove(e); + userTaskInstanceEntity.getReassignmentTimers().remove(e); + }); + entities.clear(); + + for (Map.Entry timer : userTaskInstance.getNotStartedReassignmentsTimers().entrySet()) { + if (!notStartedReassignments.contains(timer.getKey())) { + TaskReassignmentTimerEntity entity = buildEntity(userTaskInstanceEntity, timer.getKey(), timer.getValue(), TaskReassignmentType.NotStarted); + entities.add(entity); + } + } + + for (Map.Entry timer : userTaskInstance.getNotCompletedReassignmentsTimers().entrySet()) { + if (!notCompletedReassignments.contains(timer.getKey())) { + TaskReassignmentTimerEntity entity = buildEntity(userTaskInstanceEntity, timer.getKey(), timer.getValue(), TaskReassignmentType.NotCompleted); + entities.add(entity); + } + } + entities.forEach(userTaskInstanceEntity.getReassignmentTimers()::add); + } + + private TaskReassignmentTimerEntity buildEntity(UserTaskInstanceEntity userTaskInstanceEntity, String jobId, Reassignment reassignment, TaskReassignmentType type) { + TaskReassignmentTimerEntity entity = new TaskReassignmentTimerEntity(); + entity.setJobId(jobId); + entity.setTaskInstance(userTaskInstanceEntity); + entity.setJavaType(Reassignment.class.getName()); + entity.setValue(JSONUtils.valueToString(reassignment).getBytes(StandardCharsets.UTF_8)); + entity.setType(type); + return entity; + } + + @Override + public void mapEntityToInstance(UserTaskInstanceEntity userTaskInstanceEntity, UserTaskInstance userTaskInstance) { + Map notStarted = new HashMap<>(); + Map notCompleted = new HashMap<>(); + for (TaskReassignmentTimerEntity entity : userTaskInstanceEntity.getReassignmentTimers()) { + Reassignment notification = (Reassignment) JSONUtils.stringTreeToValue(new String(entity.getValue()), Reassignment.class.getName()); + switch (entity.getType()) { + case NotCompleted: + notCompleted.put(entity.getJobId(), notification); + break; + case NotStarted: + notStarted.put(entity.getJobId(), notification); + break; + } + } + + ((DefaultUserTaskInstance) userTaskInstance).setNotStartedReassignmentsTimers(notStarted); + ((DefaultUserTaskInstance) userTaskInstance).setNotCompletedReassignmentsTimers(notCompleted); + } + +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapper.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapper.java index e03125742ba..c183ad0f22b 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapper.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapper.java @@ -19,6 +19,8 @@ package org.jbpm.usertask.jpa.mapper; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; @@ -28,19 +30,11 @@ public class UserTaskInstanceEntityMapper { - private final AttachmentsEntityMapper attachmentsMapper; - private final CommentsEntityMapper commentMapper; - private final TaskInputsEntityMapper taskInputsMapper; - private final TaskOutputsEntityMapper taskOutputsMapper; - private final TaskMetadataEntityMapper taskMetadataMapper; - - public UserTaskInstanceEntityMapper(AttachmentsEntityMapper attachmentsMapper, CommentsEntityMapper commentsMapper, TaskMetadataEntityMapper taskMetadataMapper, - TaskInputsEntityMapper taskInputsMapper, TaskOutputsEntityMapper taskOutputMapper) { - this.attachmentsMapper = attachmentsMapper; - this.commentMapper = commentsMapper; - this.taskMetadataMapper = taskMetadataMapper; - this.taskInputsMapper = taskInputsMapper; - this.taskOutputsMapper = taskOutputMapper; + private List mappers; + + public UserTaskInstanceEntityMapper(Iterable mappers) { + this.mappers = new ArrayList<>(); + mappers.forEach(this.mappers::add); } public UserTaskInstanceEntity mapTaskInstanceToEntity(UserTaskInstance userTaskInstance, UserTaskInstanceEntity entity) { @@ -60,11 +54,7 @@ public UserTaskInstanceEntity mapTaskInstanceToEntity(UserTaskInstance userTaskI entity.setAdminGroups(Set.copyOf(userTaskInstance.getAdminGroups())); entity.setExcludedUsers(Set.copyOf(userTaskInstance.getExcludedUsers())); - attachmentsMapper.mapInstanceToEntity(userTaskInstance, entity); - commentMapper.mapInstanceToEntity(userTaskInstance, entity); - taskInputsMapper.mapInstanceToEntity(userTaskInstance, entity); - taskOutputsMapper.mapInstanceToEntity(userTaskInstance, entity); - taskMetadataMapper.mapInstanceToEntity(userTaskInstance, entity); + mappers.forEach(e -> e.mapInstanceToEntity(userTaskInstance, entity)); return entity; } @@ -90,11 +80,7 @@ public UserTaskInstance mapTaskEntityToInstance(UserTaskInstanceEntity entity) { instance.setAdminGroups(Set.copyOf(entity.getAdminGroups())); instance.setExcludedUsers(Set.copyOf(entity.getExcludedUsers())); - attachmentsMapper.mapEntityToInstance(entity, instance); - commentMapper.mapEntityToInstance(entity, instance); - taskInputsMapper.mapEntityToInstance(entity, instance); - taskOutputsMapper.mapEntityToInstance(entity, instance); - taskMetadataMapper.mapEntityToInstance(entity, instance); + mappers.forEach(e -> e.mapEntityToInstance(entity, instance)); return instance; } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/json/utils/JSONUtils.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/json/utils/JSONUtils.java index b7e33589800..ab4d13f49db 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/json/utils/JSONUtils.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/mapper/json/utils/JSONUtils.java @@ -22,9 +22,12 @@ import java.util.Objects; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import static java.lang.Thread.currentThread; + public class JSONUtils { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @@ -33,6 +36,10 @@ public class JSONUtils { OBJECT_MAPPER.registerModule(new JavaTimeModule()); } + public static JavaType buildJavaType(Class clazz, Class parameter) { + return OBJECT_MAPPER.getTypeFactory().constructParametricType(clazz, parameter); + } + public static String valueToString(Object value) { try { return OBJECT_MAPPER.writeValueAsString(value); @@ -46,7 +53,18 @@ public static Object stringTreeToValue(String value, String javaType) { if (Objects.isNull(value) || Objects.isNull(javaType)) { return null; } - return OBJECT_MAPPER.readValue(value, Class.forName(javaType)); + return OBJECT_MAPPER.readValue(value, currentThread().getContextClassLoader().loadClass(javaType)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static Object stringTreeToValue(String value, JavaType javaType) { + try { + if (Objects.isNull(value) || Objects.isNull(javaType)) { + return null; + } + return OBJECT_MAPPER.readValue(value, javaType); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/AbstractTaskEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/AbstractTaskEntity.java new file mode 100644 index 00000000000..9a1fed04b4d --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/AbstractTaskEntity.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.util.Objects; + +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class AbstractTaskEntity { + + @Column(name = "value") + protected T value; + + @Column(name = "java_type") + protected String javaType; + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + } + + public String getJavaType() { + return javaType; + } + + public void setJavaType(String javaType) { + this.javaType = javaType; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + AbstractTaskEntity that = (AbstractTaskEntity) o; + return Objects.equals(getValue(), that.getValue()) && Objects.equals(getJavaType(), that.getJavaType()); + } + + @Override + public int hashCode() { + return Objects.hash(getValue(), getJavaType()); + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineEntity.java new file mode 100644 index 00000000000..35f790adb1d --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineEntity.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.util.Objects; + +import jakarta.persistence.AssociationOverride; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; + +@Entity +@Table(name = "jbpm_user_tasks_deadline") +@AttributeOverrides({ + @AttributeOverride(name = "value", column = @Column(name = "notification_value")) +}) +@AssociationOverride(name = "taskInstance", + joinColumns = @JoinColumn(name = "task_id", foreignKey = @ForeignKey(name = "jbpm_user_tasks_deadline_tid"))) +public class TaskDeadlineEntity extends TaskTimerConfigEntity { + + @Column(name = "notification_type") + @Enumerated(EnumType.STRING) + private TaskDeadlineType type; + + public TaskDeadlineType getType() { + return type; + } + + public void setType(TaskDeadlineType type) { + this.type = type; + } + + @Override + public int hashCode() { + return Objects.hash(value, javaType, type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TaskDeadlineEntity other = (TaskDeadlineEntity) obj; + return type == other.type && super.equals(obj); + } + + @Override + public String toString() { + return "TaskDeadlineEntity [type=" + type + ", id=" + id + ", taskInstance=" + taskInstance.getId() + ", javaType=" + javaType + "]"; + } + +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineTimerEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineTimerEntity.java new file mode 100644 index 00000000000..0c3d762f225 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineTimerEntity.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.util.Objects; + +import jakarta.persistence.AssociationOverride; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; + +@Entity +@Table(name = "jbpm_user_tasks_deadline_timer") +@AttributeOverrides({ + @AttributeOverride(name = "jobId", column = @Column(name = "notification_job_id")), + @AttributeOverride(name = "value", column = @Column(name = "notification_value")) +}) +@AssociationOverride(name = "taskInstance", + joinColumns = @JoinColumn(name = "task_id", foreignKey = @ForeignKey(name = "jbpm_user_tasks_deadline_timer_tid"))) +public class TaskDeadlineTimerEntity extends TaskTimerDataEntity { + + @Column(name = "notification_type") + @Enumerated(EnumType.STRING) + private TaskDeadlineType type; + + public TaskDeadlineType getType() { + return type; + } + + public void setType(TaskDeadlineType type) { + this.type = type; + } + + @Override + public int hashCode() { + return Objects.hash(getJobId(), getValue(), getJavaType(), type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TaskDeadlineTimerEntity other = (TaskDeadlineTimerEntity) obj; + return super.equals(obj) && type == other.type; + } + +} diff --git a/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayConfiguration.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineType.java similarity index 80% rename from addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayConfiguration.java rename to addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineType.java index c99dba53b8b..f388f935d6a 100644 --- a/addons/common/flyway/src/main/java/org/kie/flyway/integration/KieFlywayConfiguration.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDeadlineType.java @@ -16,14 +16,9 @@ * specific language governing permissions and limitations * under the License. */ +package org.jbpm.usertask.jpa.model; -package org.kie.flyway.integration; - -import java.util.Map; - -public interface KieFlywayConfiguration { - - boolean isEnabled(); - - Map getModules(); +public enum TaskDeadlineType { + NotStarted, + NotCompleted } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskInputEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskInputEntity.java index 9c4351cfd24..3439ed45fd6 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskInputEntity.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskInputEntity.java @@ -19,7 +19,13 @@ package org.jbpm.usertask.jpa.model; -import jakarta.persistence.*; +import jakarta.persistence.AssociationOverride; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.Table; @Entity @Table(name = "jbpm_user_tasks_inputs") @@ -28,7 +34,6 @@ @AttributeOverride(name = "value", column = @Column(name = "input_value")) }) @AssociationOverride(name = "taskInstance", foreignKey = @ForeignKey(name = "jbpm_user_tasks_inputs_tid")) -@IdClass(TaskDataEntityPK.class) -public class TaskInputEntity extends TaskDataEntity { +public class TaskInputEntity extends TaskNamedDataEntity { } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskMetadataEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskMetadataEntity.java index 2729ee68716..b7d05701c81 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskMetadataEntity.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskMetadataEntity.java @@ -19,7 +19,14 @@ package org.jbpm.usertask.jpa.model; -import jakarta.persistence.*; +import jakarta.persistence.AssociationOverride; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; @Entity @Table(name = "jbpm_user_tasks_metadata") @@ -29,7 +36,6 @@ }) @AssociationOverride(name = "taskInstance", joinColumns = @JoinColumn(name = "task_id", foreignKey = @ForeignKey(name = "jbpm_user_tasks_metadata_tid"))) -@IdClass(TaskDataEntityPK.class) -public class TaskMetadataEntity extends TaskDataEntity { +public class TaskMetadataEntity extends TaskNamedDataEntity { } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDataEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskNamedDataEntity.java similarity index 66% rename from addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDataEntity.java rename to addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskNamedDataEntity.java index 506f3bf925b..45b1c9f6016 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDataEntity.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskNamedDataEntity.java @@ -21,25 +21,25 @@ import java.util.Objects; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; @MappedSuperclass -public abstract class TaskDataEntity { - - @Id - @Column(name = "name") - protected String name; +@IdClass(TaskNamedDataEntityPK.class) +public abstract class TaskNamedDataEntity extends AbstractTaskEntity { @Id @ManyToOne(optional = false) @JoinColumn(name = "task_id") protected UserTaskInstanceEntity taskInstance; - @Column(name = "value") - protected T value; - - @Column(name = "java_type") - protected String javaType; + @Id + @Column(name = "name") + protected String name; public String getName() { return name; @@ -57,35 +57,20 @@ public void setTaskInstance(UserTaskInstanceEntity taskInstance) { this.taskInstance = taskInstance; } - public T getValue() { - return value; - } - - public void setValue(T value) { - this.value = value; - } - - public String getJavaType() { - return javaType; - } - - public void setJavaType(String javaType) { - this.javaType = javaType; - } - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - TaskDataEntity that = (TaskDataEntity) o; - return Objects.equals(getName(), that.getName()) && Objects.equals(getTaskInstance(), that.getTaskInstance()) && Objects.equals(getValue(), - that.getValue()) && Objects.equals(getJavaType(), that.getJavaType()); + TaskNamedDataEntity that = (TaskNamedDataEntity) o; + return Objects.equals(getName(), that.getName()) && + Objects.equals(getValue(), that.getValue()) && + Objects.equals(getJavaType(), that.getJavaType()); } @Override public int hashCode() { - return Objects.hash(getName(), getTaskInstance(), getValue(), getJavaType()); + return Objects.hash(getName(), getValue(), getJavaType()); } } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDataEntityPK.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskNamedDataEntityPK.java similarity index 78% rename from addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDataEntityPK.java rename to addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskNamedDataEntityPK.java index 4d0f1fab9ed..1c8c4f5ce5e 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskDataEntityPK.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskNamedDataEntityPK.java @@ -22,15 +22,17 @@ import java.io.Serializable; import java.util.Objects; -public class TaskDataEntityPK implements Serializable { +public class TaskNamedDataEntityPK implements Serializable { + + private static final long serialVersionUID = 5506586793841760884L; private String name; private UserTaskInstanceEntity taskInstance; - public TaskDataEntityPK() { + public TaskNamedDataEntityPK() { } - public TaskDataEntityPK(String inputName, UserTaskInstanceEntity taskInstance) { + public TaskNamedDataEntityPK(String inputName, UserTaskInstanceEntity taskInstance) { this.taskInstance = taskInstance; this.name = inputName; } @@ -57,8 +59,8 @@ public boolean equals(Object o) { return true; if (o == null || getClass() != o.getClass()) return false; - TaskDataEntityPK that = (TaskDataEntityPK) o; - return Objects.equals(getName(), that.getName()) && Objects.equals(getTaskInstance(), that.getTaskInstance()); + TaskNamedDataEntityPK that = (TaskNamedDataEntityPK) o; + return Objects.equals(getName(), that.getName()) && Objects.equals(getTaskInstance().getId(), that.getTaskInstance().getId()); } @Override @@ -68,8 +70,8 @@ public int hashCode() { @Override public String toString() { - return "TaskInputEntityId{" + - "taskInstance='" + taskInstance + '\'' + + return "TaskNamedDataEntityId {" + + "taskInstance='" + taskInstance.getId() + '\'' + ", name='" + name + '\'' + '}'; } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskOutputEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskOutputEntity.java index bd7098f2450..7961dcecc97 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskOutputEntity.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskOutputEntity.java @@ -19,7 +19,14 @@ package org.jbpm.usertask.jpa.model; -import jakarta.persistence.*; +import jakarta.persistence.AssociationOverride; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; @Entity @Table(name = "jbpm_user_tasks_outputs") @@ -29,7 +36,6 @@ }) @AssociationOverride(name = "taskInstance", joinColumns = @JoinColumn(name = "task_id", foreignKey = @ForeignKey(name = "jbpm_user_tasks_outputs_tid"))) -@IdClass(TaskDataEntityPK.class) -public class TaskOutputEntity extends TaskDataEntity { +public class TaskOutputEntity extends TaskNamedDataEntity { } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentEntity.java new file mode 100644 index 00000000000..9bd409cd7d7 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentEntity.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.util.Objects; + +import jakarta.persistence.AssociationOverride; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; + +@Entity +@Table(name = "jbpm_user_tasks_reassignment") +@AttributeOverrides({ + @AttributeOverride(name = "value", column = @Column(name = "reassignment_value")) +}) +@AssociationOverride(name = "taskInstance", + joinColumns = @JoinColumn(name = "task_id", foreignKey = @ForeignKey(name = "jbpm_user_tasks_reassignment_tid"))) +public class TaskReassignmentEntity extends TaskTimerConfigEntity { + + @Column(name = "reassignment_type") + @Enumerated(EnumType.STRING) + private TaskReassignmentType type; + + public TaskReassignmentType getType() { + return type; + } + + public void setType(TaskReassignmentType type) { + this.type = type; + } + + @Override + public int hashCode() { + return Objects.hash(value, javaType, type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TaskReassignmentEntity other = (TaskReassignmentEntity) obj; + return type == other.type && super.equals(obj); + } + + @Override + public String toString() { + return "TaskReassignmentEntity [type=" + type + ", id=" + id + ", taskInstance=" + taskInstance.getId() + ", javaType=" + javaType + "]"; + } + +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentTimerEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentTimerEntity.java new file mode 100644 index 00000000000..8b735917c51 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentTimerEntity.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.util.Objects; + +import jakarta.persistence.AssociationOverride; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; + +@Entity +@Table(name = "jbpm_user_tasks_reassignment_timer") +@AttributeOverrides({ + @AttributeOverride(name = "jobId", column = @Column(name = "reassignment_job_id")), + @AttributeOverride(name = "value", column = @Column(name = "reassignment_value")) +}) +@AssociationOverride(name = "taskInstance", + joinColumns = @JoinColumn(name = "task_id", foreignKey = @ForeignKey(name = "jbpm_user_tasks_reassignment_timer_tid"))) +public class TaskReassignmentTimerEntity extends TaskTimerDataEntity { + + @Column(name = "reassignment_type") + @Enumerated(EnumType.STRING) + private TaskReassignmentType type; + + public TaskReassignmentType getType() { + return type; + } + + public void setType(TaskReassignmentType type) { + this.type = type; + } + + @Override + public int hashCode() { + return Objects.hash(getJobId(), getValue(), getJavaType(), type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TaskReassignmentTimerEntity other = (TaskReassignmentTimerEntity) obj; + return super.equals(obj) && type == other.type; + } +} diff --git a/kogito-workitems/kogito-jackson-utils/src/test/java/org/kie/kogito/jackson/utils/Person.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentType.java similarity index 72% rename from kogito-workitems/kogito-jackson-utils/src/test/java/org/kie/kogito/jackson/utils/Person.java rename to addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentType.java index 7f6bc505df8..bf4e2a8da26 100644 --- a/kogito-workitems/kogito-jackson-utils/src/test/java/org/kie/kogito/jackson/utils/Person.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskReassignmentType.java @@ -16,24 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.kie.kogito.jackson.utils; +package org.jbpm.usertask.jpa.model; -public class Person { - - private String name; - - public Person() { - } - - public Person(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } +public enum TaskReassignmentType { + NotStarted, + NotCompleted } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerConfigEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerConfigEntity.java new file mode 100644 index 00000000000..1f59855a808 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerConfigEntity.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.util.Objects; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class TaskTimerConfigEntity extends AbstractTaskEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + protected Integer id; + + @ManyToOne(optional = false) + @JoinColumn(name = "task_id") + protected UserTaskInstanceEntity taskInstance; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public UserTaskInstanceEntity getTaskInstance() { + return taskInstance; + } + + public void setTaskInstance(UserTaskInstanceEntity taskInstance) { + this.taskInstance = taskInstance; + } + + @Override + public int hashCode() { + return Objects.hash(value, javaType, id, taskInstance.getId()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TaskTimerConfigEntity other = (TaskTimerConfigEntity) obj; + return super.equals(obj) && Objects.equals(id, other.id) && Objects.equals(taskInstance.getId(), other.taskInstance.getId()); + } + +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerDataEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerDataEntity.java new file mode 100644 index 00000000000..67b7e21729b --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerDataEntity.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.util.Objects; + +import jakarta.persistence.Column; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +@IdClass(TaskTimerDataEntityPK.class) +public abstract class TaskTimerDataEntity extends AbstractTaskEntity { + + @Id + @ManyToOne(optional = false) + @JoinColumn(name = "task_id") + protected UserTaskInstanceEntity taskInstance; + + @Id + @Column(name = "jobId") + protected String jobId; + + public String getJobId() { + return jobId; + } + + public void setJobId(String jobId) { + this.jobId = jobId; + } + + public UserTaskInstanceEntity getTaskInstance() { + return taskInstance; + } + + public void setTaskInstance(UserTaskInstanceEntity taskInstance) { + this.taskInstance = taskInstance; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + TaskTimerDataEntity that = (TaskTimerDataEntity) o; + return Objects.equals(getJobId(), that.getJobId()) && super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(getJobId(), getValue(), getJavaType()); + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerDataEntityPK.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerDataEntityPK.java new file mode 100644 index 00000000000..d4c1c06ea69 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/TaskTimerDataEntityPK.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.model; + +import java.io.Serializable; +import java.util.Objects; + +public class TaskTimerDataEntityPK implements Serializable { + + private static final long serialVersionUID = 5506586793841760884L; + + private String jobId; + private UserTaskInstanceEntity taskInstance; + + public TaskTimerDataEntityPK() { + } + + public TaskTimerDataEntityPK(String jobId, UserTaskInstanceEntity taskInstance) { + this.taskInstance = taskInstance; + this.jobId = jobId; + } + + public UserTaskInstanceEntity getTaskInstance() { + return taskInstance; + } + + public void setTaskInstance(UserTaskInstanceEntity taskInstance) { + this.taskInstance = taskInstance; + } + + public String getJobId() { + return jobId; + } + + public void setJobId(String jobId) { + this.jobId = jobId; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + TaskTimerDataEntityPK that = (TaskTimerDataEntityPK) o; + return Objects.equals(getJobId(), that.getJobId()) && Objects.equals(getTaskInstance().getId(), that.getTaskInstance().getId()); + } + + @Override + public int hashCode() { + return Objects.hash(getJobId(), getTaskInstance()); + } + + @Override + public String toString() { + return "TaskTimerDataEntityId {" + + "taskInstance='" + taskInstance.getId() + '\'' + + ", name='" + jobId + '\'' + + '}'; + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/UserTaskInstanceEntity.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/UserTaskInstanceEntity.java index 8b3cfca0282..b721f0ce3fa 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/UserTaskInstanceEntity.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/model/UserTaskInstanceEntity.java @@ -19,9 +19,25 @@ package org.jbpm.usertask.jpa.model; -import java.util.*; - -import jakarta.persistence.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; @Entity @NamedQuery(name = UserTaskInstanceEntity.GET_INSTANCES_BY_IDENTITY, @@ -105,6 +121,18 @@ public class UserTaskInstanceEntity { @OneToMany(mappedBy = "taskInstance", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) private List metadata = new ArrayList<>(); + @OneToMany(mappedBy = "taskInstance", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private List deadlines; + + @OneToMany(mappedBy = "taskInstance", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private List deadlineTimers; + + @OneToMany(mappedBy = "taskInstance", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private List reassignments; + + @OneToMany(mappedBy = "taskInstance", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private List reassignmentTimers; + public String getId() { return id; } @@ -331,4 +359,62 @@ public void setTerminationType(String terminationType) { public String getTerminationType() { return terminationType; } + + public List getDeadlines() { + return deadlines; + } + + public void setDeadlines(List deadlines) { + this.deadlines = deadlines; + } + + public List getReassignments() { + return reassignments; + } + + public void setReassignments(List reassignments) { + this.reassignments = reassignments; + } + + public List getDeadlineTimers() { + return deadlineTimers; + } + + public void setDeadlineTimers(List deadlineTimers) { + this.deadlineTimers = deadlineTimers; + } + + public List getReassignmentTimers() { + return reassignmentTimers; + } + + public void setReassignmentTimers(List reassignmentTimers) { + this.reassignmentTimers = reassignmentTimers; + } + + @Override + public int hashCode() { + return Objects.hash(actualOwner, adminGroups, adminUsers, attachments, comments, deadlineTimers, deadlines, excludedUsers, externalReferenceId, id, inputs, metadata, outputs, potentialGroups, + potentialUsers, reassignmentTimers, reassignments, status, taskDescription, taskName, taskPriority, terminationType, userTaskId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + UserTaskInstanceEntity other = (UserTaskInstanceEntity) obj; + return Objects.equals(actualOwner, other.actualOwner) && Objects.equals(adminGroups, other.adminGroups) && Objects.equals(adminUsers, other.adminUsers) + && Objects.equals(attachments, other.attachments) && Objects.equals(comments, other.comments) && Objects.equals(deadlineTimers, other.deadlineTimers) + && Objects.equals(deadlines, other.deadlines) && Objects.equals(excludedUsers, other.excludedUsers) && Objects.equals(externalReferenceId, other.externalReferenceId) + && Objects.equals(id, other.id) && Objects.equals(inputs, other.inputs) && Objects.equals(metadata, other.metadata) && Objects.equals(outputs, other.outputs) + && Objects.equals(potentialGroups, other.potentialGroups) && Objects.equals(potentialUsers, other.potentialUsers) && Objects.equals(reassignmentTimers, other.reassignmentTimers) + && Objects.equals(reassignments, other.reassignments) && Objects.equals(status, other.status) && Objects.equals(taskDescription, other.taskDescription) + && Objects.equals(taskName, other.taskName) && Objects.equals(taskPriority, other.taskPriority) && Objects.equals(terminationType, other.terminationType) + && Objects.equals(userTaskId, other.userTaskId); + } + } diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskDeadlineRepository.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskDeadlineRepository.java new file mode 100644 index 00000000000..2fc7cedc956 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskDeadlineRepository.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.repository; + +import org.jbpm.usertask.jpa.model.TaskDeadlineEntity; + +public class TaskDeadlineRepository extends BaseRepository { + + public TaskDeadlineRepository(UserTaskJPAContext context) { + super(context); + } + + @Override + public Class getEntityClass() { + return TaskDeadlineEntity.class; + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskDeadlineTimerRepository.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskDeadlineTimerRepository.java new file mode 100644 index 00000000000..370f9e35917 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskDeadlineTimerRepository.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.repository; + +import org.jbpm.usertask.jpa.model.TaskDeadlineTimerEntity; +import org.jbpm.usertask.jpa.model.TaskTimerDataEntityPK; + +public class TaskDeadlineTimerRepository extends BaseRepository { + + public TaskDeadlineTimerRepository(UserTaskJPAContext context) { + super(context); + } + + @Override + public Class getEntityClass() { + return TaskDeadlineTimerEntity.class; + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskInputRepository.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskInputRepository.java index 6e85081ff76..344fbba90a6 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskInputRepository.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskInputRepository.java @@ -19,10 +19,10 @@ package org.jbpm.usertask.jpa.repository; -import org.jbpm.usertask.jpa.model.TaskDataEntityPK; import org.jbpm.usertask.jpa.model.TaskInputEntity; +import org.jbpm.usertask.jpa.model.TaskNamedDataEntityPK; -public class TaskInputRepository extends BaseRepository { +public class TaskInputRepository extends BaseRepository { public TaskInputRepository(UserTaskJPAContext context) { super(context); diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskMetadataRepository.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskMetadataRepository.java index 26796bb38aa..836fa5dd077 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskMetadataRepository.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskMetadataRepository.java @@ -19,10 +19,10 @@ package org.jbpm.usertask.jpa.repository; -import org.jbpm.usertask.jpa.model.TaskDataEntityPK; import org.jbpm.usertask.jpa.model.TaskMetadataEntity; +import org.jbpm.usertask.jpa.model.TaskNamedDataEntityPK; -public class TaskMetadataRepository extends BaseRepository { +public class TaskMetadataRepository extends BaseRepository { public TaskMetadataRepository(UserTaskJPAContext context) { super(context); diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskOutputRepository.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskOutputRepository.java index 2b39b8f1bb0..610cde606d2 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskOutputRepository.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskOutputRepository.java @@ -19,10 +19,10 @@ package org.jbpm.usertask.jpa.repository; -import org.jbpm.usertask.jpa.model.TaskDataEntityPK; +import org.jbpm.usertask.jpa.model.TaskNamedDataEntityPK; import org.jbpm.usertask.jpa.model.TaskOutputEntity; -public class TaskOutputRepository extends BaseRepository { +public class TaskOutputRepository extends BaseRepository { public TaskOutputRepository(UserTaskJPAContext context) { super(context); diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskReassignmentRepository.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskReassignmentRepository.java new file mode 100644 index 00000000000..ae01f0fd44b --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskReassignmentRepository.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.repository; + +import org.jbpm.usertask.jpa.model.TaskReassignmentEntity; + +public class TaskReassignmentRepository extends BaseRepository { + + public TaskReassignmentRepository(UserTaskJPAContext context) { + super(context); + } + + @Override + public Class getEntityClass() { + return TaskReassignmentEntity.class; + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskReassignmentTimerRepository.java b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskReassignmentTimerRepository.java new file mode 100644 index 00000000000..f9124609db5 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/repository/TaskReassignmentTimerRepository.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.repository; + +import org.jbpm.usertask.jpa.model.TaskReassignmentTimerEntity; +import org.jbpm.usertask.jpa.model.TaskTimerDataEntityPK; + +public class TaskReassignmentTimerRepository extends BaseRepository { + + public TaskReassignmentTimerRepository(UserTaskJPAContext context) { + super(context); + } + + @Override + public Class getEntityClass() { + return TaskReassignmentTimerEntity.class; + } +} diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/resources/kie-flyway/db/user-tasks/h2/V1.0.1__jBPM_user_task_timers.sql b/addons/common/jbpm-usertask-storage-jpa/src/main/resources/kie-flyway/db/user-tasks/h2/V1.0.1__jBPM_user_task_timers.sql new file mode 100644 index 00000000000..80154790b04 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/resources/kie-flyway/db/user-tasks/h2/V1.0.1__jBPM_user_task_timers.sql @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +create table jbpm_user_tasks_deadline ( + id int, + task_id varchar(50) not null, + notification_type varchar(255) not null, + notification_value varbinary(max), + java_type varchar(255), + primary key (id) +); + + +create table jbpm_user_tasks_reassignment ( + id int, + task_id varchar(50) not null, + reassignment_type varchar(255) not null, + reassignment_value varbinary(max), + java_type varchar(255), + primary key (id) +); + + +create table jbpm_user_tasks_deadline_timer ( + task_id varchar(50) not null, + notification_job_id varchar(255) not null, + notification_type varchar(255) not null, + notification_value varbinary(max), + java_type varchar(255), + primary key (task_id, notification_job_id) +); + +create table jbpm_user_tasks_reassignment_timer ( + task_id varchar(50) not null, + reassignment_job_id varchar(255) not null, + reassignment_type varchar(255) not null, + reassignment_value varbinary(max), + java_type varchar(255), + primary key (task_id, reassignment_job_id) +); + +CREATE SEQUENCE jbpm_user_tasks_deadline_seq INCREMENT BY 50; +CREATE SEQUENCE jbpm_user_tasks_reassignment_seq INCREMENT BY 50; + +alter table if exists jbpm_user_tasks_deadline +add constraint fk_jbpm_user_tasks_deadline_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; + +alter table if exists jbpm_user_tasks_reassignment +add constraint fk_jbpm_user_tasks_reassignment_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; + +alter table if exists jbpm_user_tasks_deadline_timer +add constraint fk_jbpm_user_tasks_deadline_timer_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; + + +alter table if exists jbpm_user_tasks_reassignment_timer +add constraint fk_jbpm_user_tasks_reassignment_timer_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; diff --git a/addons/common/jbpm-usertask-storage-jpa/src/main/resources/kie-flyway/db/user-tasks/postgresql/V1.0.1__jBPM_user_task_timers.sql b/addons/common/jbpm-usertask-storage-jpa/src/main/resources/kie-flyway/db/user-tasks/postgresql/V1.0.1__jBPM_user_task_timers.sql new file mode 100644 index 00000000000..e54054517d0 --- /dev/null +++ b/addons/common/jbpm-usertask-storage-jpa/src/main/resources/kie-flyway/db/user-tasks/postgresql/V1.0.1__jBPM_user_task_timers.sql @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +create table jbpm_user_tasks_deadline ( + id int, + task_id varchar(50) not null, + notification_type varchar(255) not null, + notification_value bytea, + java_type varchar(255), + primary key (id) +); + + +create table jbpm_user_tasks_reassignment ( + id int, + task_id varchar(50) not null, + reassignment_type varchar(255) not null, + reassignment_value bytea, + java_type varchar(255), + primary key (id) +); + + +create table jbpm_user_tasks_deadline_timer ( + task_id varchar(50) not null, + notification_job_id varchar(255) not null, + notification_type varchar(255) not null, + notification_value bytea, + java_type varchar(255), + primary key (task_id, notification_job_id) +); + + + +create table jbpm_user_tasks_reassignment_timer ( + task_id varchar(50) not null, + reassignment_job_id varchar(255) not null, + reassignment_type varchar(255) not null, + reassignment_value bytea, + java_type varchar(255), + primary key (task_id, reassignment_job_id) +); + +CREATE SEQUENCE jbpm_user_tasks_deadline_seq INCREMENT BY 50 OWNED BY jbpm_user_tasks_deadline.id; +CREATE SEQUENCE jbpm_user_tasks_reassignment_seq INCREMENT BY 50 OWNED BY jbpm_user_tasks_reassignment.id; + +alter table if exists jbpm_user_tasks_deadline +add constraint fk_jbpm_user_tasks_deadline_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; + +alter table if exists jbpm_user_tasks_reassignment +add constraint fk_jbpm_user_tasks_reassignment_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; + +alter table if exists jbpm_user_tasks_deadline_timer +add constraint fk_jbpm_user_tasks_deadline_timer_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; + +alter table if exists jbpm_user_tasks_reassignment_timer +add constraint fk_jbpm_user_tasks_reassignment_timer_tid foreign key (task_id) references jbpm_user_tasks(id) on delete cascade; diff --git a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/JPAUserTaskInstancesTest.java b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/JPAUserTaskInstancesTest.java index d0ca49f1143..b93b54e51ef 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/JPAUserTaskInstancesTest.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/JPAUserTaskInstancesTest.java @@ -24,7 +24,12 @@ import java.util.function.Function; import org.assertj.core.api.Assertions; -import org.jbpm.usertask.jpa.mapper.*; +import org.jbpm.usertask.jpa.mapper.CommentsEntityMapper; +import org.jbpm.usertask.jpa.mapper.EntityMapper; +import org.jbpm.usertask.jpa.mapper.TaskInputsEntityMapper; +import org.jbpm.usertask.jpa.mapper.TaskMetadataEntityMapper; +import org.jbpm.usertask.jpa.mapper.TaskOutputsEntityMapper; +import org.jbpm.usertask.jpa.mapper.UserTaskInstanceEntityMapper; import org.jbpm.usertask.jpa.mapper.utils.TestUtils; import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; import org.jbpm.usertask.jpa.repository.UserTaskInstanceRepository; @@ -37,7 +42,11 @@ import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class JPAUserTaskInstancesTest { @@ -46,7 +55,7 @@ public class JPAUserTaskInstancesTest { private UserTaskInstanceRepository userTaskInstanceRepository; @Mock - private AttachmentsEntityMapper attachmentsEntityMapper; + private EntityMapper attachmentsEntityMapper; @Mock private CommentsEntityMapper commentsEntityMapper; @Mock @@ -66,7 +75,7 @@ public class JPAUserTaskInstancesTest { @BeforeEach public void setup() { - userTaskInstanceEntityMapper = spy(new UserTaskInstanceEntityMapper(attachmentsEntityMapper, commentsEntityMapper, metadataEntityMapper, inputsEntityMapper, outputsEntityMapper)); + userTaskInstanceEntityMapper = spy(new UserTaskInstanceEntityMapper(List.of(attachmentsEntityMapper, commentsEntityMapper, metadataEntityMapper, inputsEntityMapper, outputsEntityMapper))); jpaUserTaskInstances = new JPAUserTaskInstances(userTaskInstanceRepository, userTaskInstanceEntityMapper); jpaUserTaskInstances.setReconnectUserTaskInstance(reconnectUserTaskInstance); jpaUserTaskInstances.setDisconnectUserTaskInstance(disconnectUserTaskInstance); diff --git a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapperTest.java b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapperTest.java index cafdb8e166b..904dd0c619b 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapperTest.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/AttachmentsEntityMapperTest.java @@ -42,7 +42,7 @@ public class AttachmentsEntityMapperTest { @Mock private AttachmentRepository repository; - private AttachmentsEntityMapper mapper; + private EntityMapper mapper; @BeforeEach public void setup() { diff --git a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapperTest.java b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapperTest.java index 3b49d9b445d..2cdf057b5d3 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapperTest.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/UserTaskInstanceEntityMapperTest.java @@ -19,6 +19,8 @@ package org.jbpm.usertask.jpa.mapper; +import java.util.List; + import org.jbpm.usertask.jpa.mapper.utils.TestUtils; import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; import org.junit.jupiter.api.BeforeEach; @@ -28,7 +30,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) public class UserTaskInstanceEntityMapperTest { @@ -48,7 +52,7 @@ public class UserTaskInstanceEntityMapperTest { @BeforeEach public void setUp() { - this.userTaskInstanceEntityMapper = new UserTaskInstanceEntityMapper(attachmentsEntityMapper, commentsEntityMapper, metadataEntityMapper, inputsEntityMapper, outputsEntityMapper); + this.userTaskInstanceEntityMapper = new UserTaskInstanceEntityMapper(List.of(attachmentsEntityMapper, commentsEntityMapper, metadataEntityMapper, inputsEntityMapper, outputsEntityMapper)); } @Test diff --git a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/utils/TestUtils.java b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/utils/TestUtils.java index b1b4373f369..0d501f1478a 100644 --- a/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/utils/TestUtils.java +++ b/addons/common/jbpm-usertask-storage-jpa/src/test/java/org/jbpm/usertask/jpa/mapper/utils/TestUtils.java @@ -19,13 +19,21 @@ package org.jbpm.usertask.jpa.mapper.utils; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; import org.assertj.core.api.Assertions; import org.jbpm.usertask.jpa.mapper.models.Person; import org.jbpm.usertask.jpa.model.AttachmentEntity; import org.jbpm.usertask.jpa.model.CommentEntity; -import org.jbpm.usertask.jpa.model.TaskDataEntity; +import org.jbpm.usertask.jpa.model.TaskNamedDataEntity; import org.jbpm.usertask.jpa.model.UserTaskInstanceEntity; import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; @@ -203,7 +211,7 @@ public static void assertUserTaskEntityMetadata(UserTaskInstanceEntity userTaskI assertUserTaskEntityMapData(userTaskInstanceEntity.getMetadata(), userTaskInstance.getMetadata()); } - private static void assertUserTaskEntityMapData(Collection entityData, Map instanceData) { + private static void assertUserTaskEntityMapData(Collection entityData, Map instanceData) { Assertions.assertThat(entityData.size()) .isEqualTo(instanceData.size()); @@ -228,18 +236,18 @@ public static void assertUserTaskInstanceMetadata(UserTaskInstance userTaskInsta assertUserTaskInstanceMapData(userTaskInstance.getMetadata(), userTaskInstanceEntity.getMetadata()); } - private static void assertUserTaskInstanceMapData(Map instanceData, Collection entityData) { + private static void assertUserTaskInstanceMapData(Map instanceData, Collection entityData) { Assertions.assertThat(instanceData.size()) .isEqualTo(entityData.size()); instanceData.forEach((key, value) -> { - Optional optional = entityData.stream().filter(data -> data.getName().equals(key)).findFirst(); + Optional optional = entityData.stream().filter(data -> data.getName().equals(key)).findFirst(); Assertions.assertThat(optional) .isPresent(); if (Objects.nonNull(value)) { - TaskDataEntity data = optional.get(); + TaskNamedDataEntity data = optional.get(); Assertions.assertThat(value.getClass().getName()) .isEqualTo(data.getJavaType()); } @@ -262,6 +270,16 @@ public static DefaultUserTaskInstance createUserTaskInstance() { instance.setAdminGroups(Set.of("Administrators", "Managers")); instance.setExcludedUsers(Set.of("Ned", "Bart")); + instance.setNotStartedDeadlines(new ArrayList<>()); + instance.setNotStartedDeadlinesTimers(new HashMap<>()); + instance.setNotCompletedDeadlines(new ArrayList<>()); + instance.setNotCompletedDeadlinesTimers(new HashMap<>()); + + instance.setNotStartedReassignments(new ArrayList<>()); + instance.setNotStartedReassignmentsTimers(new HashMap<>()); + instance.setNotCompletedReassignments(new ArrayList<>()); + instance.setNotCompletedReassignmentsTimers(new HashMap<>()); + instance.setExternalReferenceId("external-reference-id"); instance.setMetadata("ProcessId", "process-id"); diff --git a/addons/common/jobs/api/src/main/java/org/kie/kogito/jobs/api/JobCallbackResourceDef.java b/addons/common/jobs/api/src/main/java/org/kie/kogito/jobs/api/JobCallbackResourceDef.java index ba5cfd6ac02..3bb36df7527 100644 --- a/addons/common/jobs/api/src/main/java/org/kie/kogito/jobs/api/JobCallbackResourceDef.java +++ b/addons/common/jobs/api/src/main/java/org/kie/kogito/jobs/api/JobCallbackResourceDef.java @@ -20,7 +20,8 @@ import java.time.temporal.ChronoUnit; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.JobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.jobs.service.api.TemporalUnit; import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipient; import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipientJsonPayloadData; @@ -53,25 +54,16 @@ public class JobCallbackResourceDef { public static final String LIMIT_DEFAULT_VALUE = "0"; - public static final String JOBS_CALLBACK_URI = "/management/jobs"; - public static final String JOBS_CALLBACK_POST_URI = "{" + PROCESS_ID + "}/instances/{" + PROCESS_INSTANCE_ID + "}/timers/{" + TIMER_ID + "}"; private JobCallbackResourceDef() { } - public static String buildCallbackURI(ProcessInstanceJobDescription description, String jobsCallbackEndpoint) { - return URIBuilder.toURI(jobsCallbackEndpoint - + JOBS_CALLBACK_URI + "/" - + description.processId() - + "/instances/" - + description.processInstanceId() - + "/timers/" - + description.timerId()) - .toString(); + public static String buildCallbackURI(JobDescription description, String jobsCallbackEndpoint) { + return URIBuilder.toURI(jobsCallbackEndpoint + description.path()).toString(); } - public static org.kie.kogito.jobs.service.api.Job buildCallbackPatternJob(ProcessInstanceJobDescription description, + public static org.kie.kogito.jobs.service.api.Job buildCallbackPatternJob(JobDescription description, String callback, ObjectMapper objectMapper) { return org.kie.kogito.jobs.service.api.Job.builder() .id(description.id()) @@ -81,25 +73,28 @@ public static org.kie.kogito.jobs.service.api.Job buildCallbackPatternJob(Proces .build(); } - private static HttpRecipient buildRecipient(ProcessInstanceJobDescription description, String callback, ObjectMapper objectMapper) { - return HttpRecipient.builder() + private static HttpRecipient buildRecipient(JobDescription description, String callback, ObjectMapper objectMapper) { + HttpRecipient.Builder selector = HttpRecipient.builder() .forJsonPayload() .payload(HttpRecipientJsonPayloadData.from(buildPayload(description, objectMapper))) .url(callback) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) - .header(PROCESS_ID, description.processId()) - .header(PROCESS_INSTANCE_ID, description.processInstanceId()) - .header(ROOT_PROCESS_ID, description.rootProcessId()) - .header(ROOT_PROCESS_INSTANCE_ID, description.rootProcessInstanceId()) - .header(NODE_INSTANCE_ID, description.nodeInstanceId()) - .build(); + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + + if (description instanceof ProcessInstanceJobDescription processInstanceJobDescription) { + selector.header(PROCESS_ID, processInstanceJobDescription.processId()) + .header(PROCESS_INSTANCE_ID, processInstanceJobDescription.processInstanceId()) + .header(ROOT_PROCESS_ID, processInstanceJobDescription.rootProcessId()) + .header(ROOT_PROCESS_INSTANCE_ID, processInstanceJobDescription.rootProcessInstanceId()) + .header(NODE_INSTANCE_ID, processInstanceJobDescription.nodeInstanceId()); + } + return selector.build(); } - private static JsonNode buildPayload(ProcessInstanceJobDescription description, ObjectMapper objectMapper) { + private static JsonNode buildPayload(JobDescription description, ObjectMapper objectMapper) { return objectMapper.valueToTree(new JobCallbackPayload(description.id())); } - public static TimerSchedule buildSchedule(ProcessInstanceJobDescription description) { + public static TimerSchedule buildSchedule(JobDescription description) { return TimerSchedule.builder() .startTime(description.expirationTime().get().toOffsetDateTime().truncatedTo(ChronoUnit.MILLIS)) .repeatCount(translateLimit(description.expirationTime().repeatLimit())) diff --git a/addons/common/jobs/api/src/test/java/org/kie/kogito/jobs/api/JobCallbackResourceDefTest.java b/addons/common/jobs/api/src/test/java/org/kie/kogito/jobs/api/JobCallbackResourceDefTest.java index ee9b7aec67e..060a0470b8f 100644 --- a/addons/common/jobs/api/src/test/java/org/kie/kogito/jobs/api/JobCallbackResourceDefTest.java +++ b/addons/common/jobs/api/src/test/java/org/kie/kogito/jobs/api/JobCallbackResourceDefTest.java @@ -23,7 +23,7 @@ import org.junit.jupiter.api.Test; import org.kie.kogito.jobs.ExactExpirationTime; import org.kie.kogito.jobs.ExpirationTime; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipient; import org.kie.kogito.jobs.service.api.schedule.timer.TimerSchedule; import org.kie.kogito.jobs.service.api.serlialization.SerializationUtils; @@ -91,7 +91,7 @@ void buildCallbackPatternJob() { } private ProcessInstanceJobDescription mockProcessInstanceJobDescription() { - return ProcessInstanceJobDescription.builder() + return ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .id(JOB_ID) .timerId(TIMER_ID) .expirationTime(EXPIRATION_TIME) diff --git a/addons/common/jobs/management-common/src/main/java/org/kie/kogito/jobs/management/RestJobsService.java b/addons/common/jobs/management-common/src/main/java/org/kie/kogito/jobs/management/RestJobsService.java index a503015d3ba..615b40b5cd9 100644 --- a/addons/common/jobs/management-common/src/main/java/org/kie/kogito/jobs/management/RestJobsService.java +++ b/addons/common/jobs/management-common/src/main/java/org/kie/kogito/jobs/management/RestJobsService.java @@ -21,8 +21,8 @@ import java.net.URI; import java.util.Objects; +import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; import org.kie.kogito.jobs.api.URIBuilder; import org.kie.kogito.jobs.service.api.Job; @@ -46,7 +46,7 @@ public RestJobsService(String jobServiceUrl, String callbackEndpoint, ObjectMapp this.objectMapper = objectMapper; } - public String getCallbackEndpoint(ProcessInstanceJobDescription description) { + public String getCallbackEndpoint(JobDescription description) { return buildCallbackURI(description, callbackEndpoint); } @@ -58,7 +58,7 @@ public URI getJobsServiceUri() { return jobsServiceUri; } - public Job buildJob(ProcessInstanceJobDescription description, String callback) { + public Job buildJob(JobDescription description, String callback) { return buildCallbackPatternJob(description, callback, objectMapper); } } diff --git a/addons/common/jobs/management-common/src/test/java/org/kie/kogito/jobs/management/RestJobsServiceTest.java b/addons/common/jobs/management-common/src/test/java/org/kie/kogito/jobs/management/RestJobsServiceTest.java index 193ceb92a1d..0f0b2d3f5de 100644 --- a/addons/common/jobs/management-common/src/test/java/org/kie/kogito/jobs/management/RestJobsServiceTest.java +++ b/addons/common/jobs/management-common/src/test/java/org/kie/kogito/jobs/management/RestJobsServiceTest.java @@ -25,9 +25,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.kie.kogito.jobs.ExactExpirationTime; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; -import org.kie.kogito.jobs.ProcessJobDescription; import org.kie.kogito.jobs.api.JobCallbackPayload; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.jobs.service.api.Job; import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipient; import org.kie.kogito.jobs.service.api.recipient.http.HttpRecipientJsonPayloadData; @@ -37,7 +36,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; public abstract class RestJobsServiceTest { @@ -64,7 +62,7 @@ void setUp() { @Test void testGetCallbackEndpoint() { - ProcessInstanceJobDescription description = ProcessInstanceJobDescription.builder() + ProcessInstanceJobDescription description = ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .id(JOB_ID) .timerId(TIMER_ID) .expirationTime(ExactExpirationTime.now()) @@ -86,17 +84,8 @@ void testGetJobsServiceUri() { assertThat(jobsServiceUri).hasToString(JOB_SERVICE_URL + "/v2/jobs"); } - @Test - void testScheduleProcessJob() { - ProcessJobDescription processJobDescription = ProcessJobDescription.of(ExactExpirationTime.of(EXPIRATION_TIME), - 1, - PROCESS_ID); - assertThatThrownBy(() -> tested.scheduleProcessJob(processJobDescription)) - .isInstanceOf(UnsupportedOperationException.class); - } - protected ProcessInstanceJobDescription buildProcessInstanceJobDescription() { - return ProcessInstanceJobDescription.builder() + return ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .id(JOB_ID) .timerId(TIMER_ID) .expirationTime(ExactExpirationTime.of(EXPIRATION_TIME)) diff --git a/api/kogito-api/src/main/java/org/kie/kogito/StaticConfig.java b/api/kogito-api/src/main/java/org/kie/kogito/StaticConfig.java index cbfbe80afa6..dd67a4cc192 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/StaticConfig.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/StaticConfig.java @@ -18,22 +18,33 @@ */ package org.kie.kogito; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import static java.util.Arrays.asList; + public class StaticConfig implements Config { - private final Addons addons; - private final Map, KogitoConfig> configMap = new HashMap<>(); + private Addons addons; + private Map, KogitoConfig> configMap = new HashMap<>(); + + public StaticConfig() { + addons = Addons.EMTPY; + } + + public StaticConfig(Addons addons, KogitoConfig... configs) { + init(addons, configs); + } + + protected StaticConfig(Addons addons, Iterable configs) { + init(addons, configs); + } - public StaticConfig(Addons addons, - KogitoConfig... configs) { - this(addons, Arrays.asList(configs)); + protected void init(Addons addons, KogitoConfig... configs) { + init(addons, asList(configs)); } - protected StaticConfig(Addons addons, - Iterable configs) { + protected void init(Addons addons, Iterable configs) { this.addons = addons; configs.forEach(this::loadConfig); } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobDescription.java b/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobDescription.java index 5bbd520bfcb..f69c74e6c2e 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobDescription.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobDescription.java @@ -20,9 +20,14 @@ public interface JobDescription { + static final String JOBS_CALLBACK_URI = "/management/jobs"; + String id(); ExpirationTime expirationTime(); Integer priority(); + + String path(); + } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java b/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java index a9e5230c40d..f953c9a79b7 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/JobsService.java @@ -29,19 +29,11 @@ public interface JobsService { * Schedules process job that is responsible for starting new process instances * based on the given description. * + * @param context of the job * @param description defines what kind of process should be started upon expiration time * @return returns unique id of the job */ - String scheduleProcessJob(ProcessJobDescription description); - - /** - * Schedules process instance related job that will signal exact same process instance - * upon expiration time. - * - * @param description defines the context of the process instance that should be signaled - * @return returns unique id of the job - */ - String scheduleProcessInstanceJob(ProcessInstanceJobDescription description); + String scheduleJob(JobDescription description); /** * Cancels given job diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessInstanceJobDescription.java b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessInstanceJobDescription.java similarity index 88% rename from api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessInstanceJobDescription.java rename to api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessInstanceJobDescription.java index 086bfac2444..3f0becb82e4 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessInstanceJobDescription.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessInstanceJobDescription.java @@ -16,7 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.kie.kogito.jobs; +package org.kie.kogito.jobs.descriptors; + +import org.kie.kogito.jobs.ExpirationTime; +import org.kie.kogito.jobs.JobDescription; import static java.util.Objects.requireNonNull; @@ -93,10 +96,21 @@ public String nodeInstanceId() { return nodeInstanceId; } - public static ProcessInstanceJobDescriptionBuilder builder() { + public static ProcessInstanceJobDescriptionBuilder newProcessInstanceJobDescriptionBuilder() { return new ProcessInstanceJobDescriptionBuilder(); } + @Override + public String path() { + return JOBS_CALLBACK_URI + "/" + + processId() + + "/instances/" + + processInstanceId() + + "/timers/" + + timerId(); + + } + @Override public String toString() { return "ProcessInstanceJobDescription{" + diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessInstanceJobDescriptionBuilder.java b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessInstanceJobDescriptionBuilder.java similarity index 97% rename from api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessInstanceJobDescriptionBuilder.java rename to api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessInstanceJobDescriptionBuilder.java index 73e491aefe9..0c9c203442a 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessInstanceJobDescriptionBuilder.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessInstanceJobDescriptionBuilder.java @@ -16,10 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.kie.kogito.jobs; +package org.kie.kogito.jobs.descriptors; import java.util.UUID; +import org.kie.kogito.jobs.ExpirationTime; + public class ProcessInstanceJobDescriptionBuilder { private String id; diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessJobDescription.java b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessJobDescription.java similarity index 93% rename from api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessJobDescription.java rename to api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessJobDescription.java index d3f3d8d177a..e8ab6baf01d 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/jobs/ProcessJobDescription.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/ProcessJobDescription.java @@ -16,10 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.kie.kogito.jobs; +package org.kie.kogito.jobs.descriptors; import java.util.UUID; +import org.kie.kogito.jobs.ExpirationTime; +import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.process.Process; import static java.util.Objects.requireNonNull; @@ -87,4 +89,9 @@ public String processId() { public Process process() { return process; } + + @Override + public String path() { + return ""; + } } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/UserTaskInstanceJobDescription.java b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/UserTaskInstanceJobDescription.java new file mode 100644 index 00000000000..5fbe5f29edc --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/UserTaskInstanceJobDescription.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.jobs.descriptors; + +import org.kie.kogito.jobs.ExpirationTime; +import org.kie.kogito.jobs.JobDescription; + +public class UserTaskInstanceJobDescription implements JobDescription { + + private String id; + private ExpirationTime expirationTime; + private Integer priority = ProcessInstanceJobDescription.DEFAULT_PRIORITY; + private String userTaskInstanceId; + + public UserTaskInstanceJobDescription() { + // do nothing + } + + public UserTaskInstanceJobDescription(String id, ExpirationTime expirationTime, Integer priority, String userTaskInstanceId) { + this.id = id; + this.expirationTime = expirationTime; + this.priority = priority; + this.userTaskInstanceId = userTaskInstanceId; + } + + @Override + public String id() { + return id; + } + + @Override + public ExpirationTime expirationTime() { + return expirationTime; + } + + @Override + public Integer priority() { + return priority; + } + + @Override + public String path() { + return null; + } + + public String getUserTaskInstanceId() { + return userTaskInstanceId; + } + + public static UserTaskInstanceJobDescriptionBuilder newUserTaskInstanceJobDescriptionBuilder() { + return new UserTaskInstanceJobDescriptionBuilder(); + } +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/UserTaskInstanceJobDescriptionBuilder.java b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/UserTaskInstanceJobDescriptionBuilder.java new file mode 100644 index 00000000000..ad8a4bfba23 --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/jobs/descriptors/UserTaskInstanceJobDescriptionBuilder.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.jobs.descriptors; + +import java.util.UUID; + +import org.kie.kogito.jobs.ExpirationTime; +import org.kie.kogito.jobs.JobDescription; + +public class UserTaskInstanceJobDescriptionBuilder implements JobDescription { + + private String id; + private ExpirationTime expirationTime; + private Integer priority = ProcessInstanceJobDescription.DEFAULT_PRIORITY; + private String userTaskInstanceId; + + @Override + public String id() { + return id; + } + + @Override + public ExpirationTime expirationTime() { + return expirationTime; + } + + @Override + public Integer priority() { + return priority; + } + + @Override + public String path() { + return null; + } + + public UserTaskInstanceJobDescriptionBuilder id(String id) { + this.id = id; + return this; + } + + public UserTaskInstanceJobDescriptionBuilder generateId() { + return id(UUID.randomUUID().toString()); + } + + public UserTaskInstanceJobDescriptionBuilder expirationTime(ExpirationTime expirationTime) { + this.expirationTime = expirationTime; + return this; + } + + public UserTaskInstanceJobDescriptionBuilder priority(Integer priority) { + this.priority = priority; + return this; + } + + public UserTaskInstanceJobDescriptionBuilder userTaskInstanceId(String userTaskInstanceId) { + this.userTaskInstanceId = userTaskInstanceId; + return this; + } + + public UserTaskInstanceJobDescription build() { + return new UserTaskInstanceJobDescription(id, expirationTime, priority, userTaskInstanceId); + } +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstanceExecutionException.java b/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstanceExecutionException.java index a535cbc9da5..04d1732b438 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstanceExecutionException.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessInstanceExecutionException.java @@ -36,7 +36,7 @@ public ProcessInstanceExecutionException(String processInstanceId, String failed } public ProcessInstanceExecutionException(String processInstanceId, String failedNodeId, String errorMessage, Throwable rootCause) { - super("Process instance with id " + processInstanceId + " failed becuase of " + errorMessage, rootCause); + super("Process instance with id " + processInstanceId + " failed because of " + errorMessage, rootCause); this.processInstanceId = processInstanceId; this.failedNodeId = failedNodeId; this.errorMessage = errorMessage; diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java index b07df5f2f35..1a8b8f80b89 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java @@ -19,10 +19,10 @@ package org.kie.kogito.usertask; import java.util.Collection; -import java.util.Map; import java.util.Set; import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Notification; import org.kie.kogito.usertask.model.Reassignment; /** @@ -101,13 +101,13 @@ public interface UserTask { */ Set getExcludedUsers(); - Collection>> getNotStartedDeadlines(); + Collection> getNotStartedDeadlines(); - Collection>> getNotCompletedDeadlines(); + Collection> getNotCompletedDeadlines(); Collection> getNotStartedReassignments(); - Collection> getNotCompletedReassigments(); + Collection> getNotCompletedReassignments(); UserTaskAssignmentStrategy getAssignmentStrategy(); diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java index e28e23befed..6fcdd84c6f0 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java @@ -23,9 +23,13 @@ import java.util.Set; import org.kie.kogito.auth.IdentityProvider; +import org.kie.kogito.jobs.descriptors.UserTaskInstanceJobDescription; import org.kie.kogito.usertask.lifecycle.UserTaskState; import org.kie.kogito.usertask.model.Attachment; import org.kie.kogito.usertask.model.Comment; +import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Notification; +import org.kie.kogito.usertask.model.Reassignment; public interface UserTaskInstance { @@ -43,6 +47,8 @@ public interface UserTaskInstance { String getActualOwner(); + void initialize(Map data, IdentityProvider identity); + void transition(String transitionId, Map data, IdentityProvider identityProvider); String getExternalReferenceId(); @@ -118,4 +124,30 @@ public interface UserTaskInstance { Collection getComments(); + void trigger(UserTaskInstanceJobDescription userTaskInstanceJobDescription); + + Collection> getNotStartedDeadlines(); + + Collection> getNotCompletedDeadlines(); + + Collection> getNotStartedReassignments(); + + Collection> getNotCompletedReassignments(); + + void startNotStartedDeadlines(); + + void startNotCompletedDeadlines(); + + void startNotStartedReassignments(); + + void startNotCompletedReassignments(); + + void stopNotStartedDeadlines(); + + void stopNotStartedReassignments(); + + void stopNotCompletedDeadlines(); + + void stopNotCompletedReassignments(); + } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java index 09170f4d1cc..43b8b669505 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java @@ -28,13 +28,23 @@ public interface UserTaskLifeCycle { + final String ACTIVATE = "activate"; + + default String startTransition() { + return ACTIVATE; + } + Optional transition(UserTaskInstance userTaskInstance, UserTaskTransitionToken transition, IdentityProvider identity); UserTaskTransitionToken newTransitionToken(String transitionId, UserTaskInstance userTaskInstance, Map data); - UserTaskTransitionToken newCompleteTransitionToken(UserTaskInstance userTaskInstance, Map emptyMap); + default Optional newReassignmentTransitionToken(UserTaskInstance defaultUserTaskInstance, Map data) { + return Optional.empty(); + } + + UserTaskTransitionToken newCompleteTransitionToken(UserTaskInstance userTaskInstance, Map data); - UserTaskTransitionToken newAbortTransitionToken(UserTaskInstance userTaskInstance, Map emptyMap); + UserTaskTransitionToken newAbortTransitionToken(UserTaskInstance userTaskInstance, Map data); List allowedTransitions(UserTaskInstance ut); diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/model/Notification.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/model/Notification.java new file mode 100644 index 00000000000..0146d3cd706 --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/model/Notification.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.usertask.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class Notification { + + private Map data; + + public Notification() { + data = new HashMap<>(); + } + + public Notification(Map data) { + this.data = data; + } + + public Map getData() { + return data; + } + + public void setData(Map data) { + this.data = data; + } + + @Override + public String toString() { + return "Notification [data=" + data + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(data); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Notification other = (Notification) obj; + return Objects.equals(data, other.data); + } + +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/model/Reassignment.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/model/Reassignment.java index 024532fcf1f..89bda5921fc 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/model/Reassignment.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/model/Reassignment.java @@ -26,6 +26,17 @@ public class Reassignment { private Set potentialUsers; private Set potentialGroups; + public Reassignment() { + } + + public void setPotentialGroups(Set potentialGroups) { + this.potentialGroups = potentialGroups; + } + + public void setPotentialUsers(Set potentialUsers) { + this.potentialUsers = potentialUsers; + } + public Reassignment(Set potentialUsers, Set potentialGroups) { this.potentialUsers = potentialUsers; this.potentialGroups = potentialGroups; diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobContext.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobContext.java new file mode 100644 index 00000000000..ed98f33ddae --- /dev/null +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobContext.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.services.jobs.impl; + +import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime; +import org.kie.kogito.process.Processes; +import org.kie.kogito.uow.UnitOfWorkManager; +import org.kie.kogito.usertask.UserTasks; + +public record InMemoryJobContext(KogitoProcessRuntime runtime, UnitOfWorkManager unitOfWorkManager, Processes processes, UserTasks userTasks) { + +} \ No newline at end of file diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java index c3ea0ad7f86..f48f8b9c2b2 100644 --- a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryJobService.java @@ -20,7 +20,9 @@ import java.time.Duration; import java.time.ZonedDateTime; -import java.util.Objects; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; @@ -28,16 +30,8 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import org.kie.kogito.Model; import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; -import org.kie.kogito.jobs.ProcessJobDescription; -import org.kie.kogito.process.Process; -import org.kie.kogito.process.ProcessInstanceOptimisticLockingException; -import org.kie.kogito.process.Processes; -import org.kie.kogito.services.uow.UnitOfWorkExecutor; -import org.kie.kogito.uow.UnitOfWorkManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,74 +39,59 @@ public class InMemoryJobService implements JobsService, AutoCloseable { public static final String IN_MEMORY_JOB_SERVICE_POOL_SIZE_PROPERTY = "kogito.in-memory.job-service.pool-size"; private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryJobService.class); - protected static final String TRIGGER = "timer"; - protected final ScheduledExecutorService scheduler; - protected final UnitOfWorkManager unitOfWorkManager; + protected ScheduledExecutorService scheduler; - protected ConcurrentHashMap> scheduledJobs = new ConcurrentHashMap<>(); - private final Processes processes; + protected ConcurrentHashMap> scheduledJobs; + protected List jobExecutorFactories; - private static final ConcurrentHashMap INSTANCE = new ConcurrentHashMap<>(); - - protected InMemoryJobService(Processes processes, UnitOfWorkManager unitOfWorkManager) { - this(processes, unitOfWorkManager, new ScheduledThreadPoolExecutor(Integer.parseInt(System.getProperty(IN_MEMORY_JOB_SERVICE_POOL_SIZE_PROPERTY, "10")))); + public InMemoryJobService() { + this(new ScheduledThreadPoolExecutor(Integer.parseInt(System.getProperty(IN_MEMORY_JOB_SERVICE_POOL_SIZE_PROPERTY, "10")))); } - protected InMemoryJobService(Processes processes, UnitOfWorkManager unitOfWorkManager, ScheduledExecutorService scheduler) { - this.processes = processes; - this.unitOfWorkManager = unitOfWorkManager; + public InMemoryJobService(ScheduledExecutorService scheduler) { + this.scheduledJobs = new ConcurrentHashMap<>(); + this.jobExecutorFactories = new ArrayList<>(); this.scheduler = scheduler; } - public static InMemoryJobService get(Processes processes, UnitOfWorkManager unitOfWorkManager) { - Objects.requireNonNull(processes); - Objects.requireNonNull(unitOfWorkManager); - return INSTANCE.computeIfAbsent(processes.hashCode() + 7 * unitOfWorkManager.hashCode(), k -> new InMemoryJobService(processes, unitOfWorkManager)); - } - - public static InMemoryJobService get(Processes processes, UnitOfWorkManager unitOfWorkManager, ScheduledExecutorService scheduler) { - Objects.requireNonNull(processes); - Objects.requireNonNull(unitOfWorkManager); - Objects.requireNonNull(scheduler); - return INSTANCE.computeIfAbsent(processes.hashCode() + 7 * unitOfWorkManager.hashCode(), k -> new InMemoryJobService(processes, unitOfWorkManager, scheduler)); + public InMemoryJobService registerJobExecutorFactory(JobExecutorFactory jobExecutorFactory) { + Iterator iterator = this.jobExecutorFactories.iterator(); + while (iterator.hasNext()) { + JobExecutorFactory factory = iterator.next(); + if (factory.types().containsAll(jobExecutorFactory.types())) { + iterator.remove(); + } + } + this.jobExecutorFactories.add(jobExecutorFactory); + return this; } @Override - public String scheduleProcessJob(ProcessJobDescription description) { - LOGGER.debug("ScheduleProcessJob: {}", description); + public String scheduleJob(JobDescription jobDescription) { + LOGGER.debug("ScheduleProcessJob: {}", jobDescription); ScheduledFuture future; - if (description.expirationTime().repeatInterval() != null) { - future = scheduler.scheduleAtFixedRate(repeatableProcessJobByDescription(description), calculateDelay(description), description.expirationTime().repeatInterval(), TimeUnit.MILLISECONDS); - } else { - future = scheduler.schedule(processJobByDescription(description), calculateDelay(description), TimeUnit.MILLISECONDS); + long delay = calculateDelay(jobDescription); + Long interval = jobDescription.expirationTime().repeatInterval(); + Optional jobExecutorFactoryFound = findJobExecutorFactory(jobDescription); + + if (jobExecutorFactoryFound.isEmpty()) { + throw new IllegalArgumentException("Could not schedule " + jobDescription + ". No job executor factory provided"); } - scheduledJobs.put(description.id(), future); - return description.id(); - } - @Override - public String scheduleProcessInstanceJob(ProcessInstanceJobDescription description) { - ScheduledFuture future; - if (description.expirationTime().repeatInterval() != null) { - future = scheduler.scheduleAtFixedRate( - getSignalProcessInstanceCommand(description, false, description.expirationTime().repeatLimit()), - calculateDelay(description), description.expirationTime().repeatInterval(), TimeUnit.MILLISECONDS); + JobExecutorFactory jobExecutorFactory = jobExecutorFactoryFound.get(); + + if (interval != null) { + future = scheduler.scheduleAtFixedRate(jobExecutorFactory.createNewRepeteableRunnable(this, jobDescription), delay, interval, TimeUnit.MILLISECONDS); } else { - future = scheduler.schedule(getSignalProcessInstanceCommand(description, true, 1), calculateDelay(description), - TimeUnit.MILLISECONDS); + future = scheduler.schedule(jobExecutorFactory.createNewRunnable(this, jobDescription), delay, TimeUnit.MILLISECONDS); } - scheduledJobs.put(description.id(), future); - return description.id(); + scheduledJobs.put(jobDescription.id(), future); + return jobDescription.id(); } - public Runnable getSignalProcessInstanceCommand(ProcessInstanceJobDescription description, boolean remove, int limit) { - return new SignalProcessInstanceOnExpiredTimer( - description.id(), - description.timerId(), - description.processInstanceId(), - remove, - limit); + private Optional findJobExecutorFactory(JobDescription jobDescription) { + return jobExecutorFactories.stream().filter(factory -> factory.accept(jobDescription)).findFirst(); } @Override @@ -139,102 +118,15 @@ protected long calculateDelay(JobDescription description) { return delay; } - protected Runnable processJobByDescription(ProcessJobDescription description) { - return new StartProcessOnExpiredTimer(description.id(), description.process(), true, -1); - } - - protected Runnable repeatableProcessJobByDescription(ProcessJobDescription description) { - return new StartProcessOnExpiredTimer(description.id(), description.process(), false, description.expirationTime().repeatLimit()); - } - - private class SignalProcessInstanceOnExpiredTimer implements Runnable { - - private final String id; - private final String timerId; - private boolean removeAtExecution; - private String processInstanceId; - private Integer limit; - - private SignalProcessInstanceOnExpiredTimer(String id, String timerId, String processInstanceId, boolean removeAtExecution, Integer limit) { - this.id = id; - this.timerId = timerId; - this.processInstanceId = processInstanceId; - this.removeAtExecution = removeAtExecution; - this.limit = limit; - } - - @Override - public void run() { - try { - Optional> process = processes.processByProcessInstanceId(processInstanceId); - if (process.isEmpty()) { - LOGGER.info("Skipping Job {}. There is no process for pid {} ", id, processInstanceId); - return; - } - LOGGER.info("Job {} started", id); - limit--; - boolean executed = new TriggerJobCommand(processInstanceId, id, timerId, limit, process.get(), unitOfWorkManager).execute(); - if (limit == 0 || !executed) { - cancelJob(id, false); - } - LOGGER.debug("Job {} completed", id); - } catch (ProcessInstanceOptimisticLockingException ex) { - LOGGER.info("Retrying Job {} due to: {}", id, ex.getMessage()); - limit++; - run(); - } finally { - if (removeAtExecution) { - cancelJob(id, false); - } - } - } - } - - private class StartProcessOnExpiredTimer implements Runnable { - - private final String id; - - private boolean removeAtExecution; - @SuppressWarnings("rawtypes") - private org.kie.kogito.process.Process process; - - private Integer limit; - - private StartProcessOnExpiredTimer(String id, org.kie.kogito.process.Process process, boolean removeAtExecution, Integer limit) { - this.id = id; - this.process = process; - this.removeAtExecution = removeAtExecution; - this.limit = limit; - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - try { - LOGGER.debug("Job {} started", id); - UnitOfWorkExecutor.executeInUnitOfWork(unitOfWorkManager, () -> { - org.kie.kogito.process.ProcessInstance pi = process.createInstance(process.createModel()); - if (pi != null) { - pi.start(TRIGGER, null); - } - return null; - }); - limit--; - if (limit == 0) { - cancelJob(id, true); - } - LOGGER.debug("Job {} completed", id); - } finally { - if (removeAtExecution) { - cancelJob(id, false); - } - } - } - } - @Override public void close() throws Exception { + LOGGER.info("closing in memory job service"); scheduledJobs.clear(); - scheduler.shutdown(); + scheduledJobs.forEach((k, v) -> v.cancel(true)); + scheduler.shutdownNow(); + } + + public void clearJobExecutorFactories() { + jobExecutorFactories.clear(); } } diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryProcessJobExecutorFactory.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryProcessJobExecutorFactory.java new file mode 100644 index 00000000000..00ed9f8f490 --- /dev/null +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryProcessJobExecutorFactory.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.services.jobs.impl; + +import java.util.Optional; +import java.util.Set; + +import org.kie.kogito.Model; +import org.kie.kogito.jobs.JobDescription; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessJobDescription; +import org.kie.kogito.process.Process; +import org.kie.kogito.process.ProcessInstanceOptimisticLockingException; +import org.kie.kogito.services.uow.UnitOfWorkExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InMemoryProcessJobExecutorFactory implements JobExecutorFactory { + + private InMemoryJobContext jobsConfiguration; + + public InMemoryProcessJobExecutorFactory(InMemoryJobContext jobsConfiguration) { + this.jobsConfiguration = jobsConfiguration; + } + + @Override + public Set> types() { + return Set.of(ProcessInstanceJobDescription.class, ProcessJobDescription.class); + } + + @Override + public Runnable createNewRunnable(JobsService jobService, JobDescription jobDescription) { + if (jobDescription instanceof ProcessInstanceJobDescription processInstanceJobDescription) { + return processInstanceJobDescription(jobService, jobsConfiguration, processInstanceJobDescription, true, 1); + } else if (jobDescription instanceof ProcessJobDescription processJobDescription) { + return processJobByDescription(jobService, jobsConfiguration, processJobDescription); + } + throw new IllegalArgumentException("single job description not supported for " + jobDescription); + } + + @Override + public Runnable createNewRepeteableRunnable(JobsService jobService, JobDescription jobDescription) { + if (jobDescription instanceof ProcessInstanceJobDescription processInstanceJobDescription) { + return processInstanceJobDescription(jobService, jobsConfiguration, processInstanceJobDescription, false, processInstanceJobDescription.expirationTime().repeatLimit()); + } else if (jobDescription instanceof ProcessJobDescription processJobDescription) { + return repeatableJobByDescription(jobService, jobsConfiguration, processJobDescription); + } + throw new IllegalArgumentException("repeteable job description not supported for " + jobDescription); + } + + protected Runnable processInstanceJobDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, ProcessInstanceJobDescription description, boolean remove, int limit) { + return new SignalProcessInstanceOnExpiredTimer( + jobService, + jobsConfiguration, + description.id(), + description.timerId(), + description.processInstanceId(), + remove, + limit); + } + + protected Runnable processJobByDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, ProcessJobDescription description) { + return new StartProcessOnExpiredTimer(jobService, jobsConfiguration, description.id(), description.process(), true, -1); + } + + protected Runnable repeatableJobByDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, ProcessJobDescription description) { + return new StartProcessOnExpiredTimer(jobService, jobsConfiguration, description.id(), description.process(), false, description.expirationTime().repeatLimit()); + } + +} + +class SignalProcessInstanceOnExpiredTimer implements Runnable { + + private static Logger LOGGER = LoggerFactory.getLogger(SignalProcessInstanceOnExpiredTimer.class); + + private final String id; + private final String timerId; + private boolean removeAtExecution; + private String processInstanceId; + private Integer limit; + private JobsService jobService; + + private InMemoryJobContext jobsConfiguration; + + public SignalProcessInstanceOnExpiredTimer(JobsService jobService, InMemoryJobContext jobsConfiguration, String id, String timerId, String processInstanceId, boolean removeAtExecution, + Integer limit) { + this.id = id; + this.timerId = timerId; + this.processInstanceId = processInstanceId; + this.removeAtExecution = removeAtExecution; + this.limit = limit; + this.jobsConfiguration = jobsConfiguration; + this.jobService = jobService; + } + + @Override + public void run() { + try { + Optional> process = jobsConfiguration.processes().processByProcessInstanceId(processInstanceId); + if (process.isEmpty()) { + LOGGER.info("Skipping Job {}. There is no process for pid {} ", id, processInstanceId); + return; + } + LOGGER.info("Job {} started", id); + limit--; + boolean executed = new TriggerJobCommand(processInstanceId, id, timerId, limit, process.get(), jobsConfiguration.unitOfWorkManager()).execute(); + if (limit == 0 || !executed) { + jobService.cancelJob(id); + } + LOGGER.debug("Job {} completed", id); + } catch (ProcessInstanceOptimisticLockingException ex) { + LOGGER.info("Retrying Job {} due to: {}", id, ex.getMessage()); + limit++; + run(); + } finally { + if (removeAtExecution) { + jobService.cancelJob(id); + } + } + } +} + +class StartProcessOnExpiredTimer implements Runnable { + + private static Logger LOGGER = LoggerFactory.getLogger(StartProcessOnExpiredTimer.class); + + private static final String TRIGGER = "timer"; + + private final String id; + + private boolean removeAtExecution; + @SuppressWarnings("rawtypes") + private org.kie.kogito.process.Process process; + + private Integer limit; + + private JobsService jobService; + private InMemoryJobContext jobsConfiguration; + + public StartProcessOnExpiredTimer(JobsService jobService, InMemoryJobContext jobsConfiguration, String id, org.kie.kogito.process.Process process, boolean removeAtExecution, Integer limit) { + this.id = id; + this.process = process; + this.removeAtExecution = removeAtExecution; + this.limit = limit; + this.jobsConfiguration = jobsConfiguration; + this.jobService = jobService; + } + + @SuppressWarnings("unchecked") + @Override + public void run() { + try { + LOGGER.debug("Job {} started", id); + UnitOfWorkExecutor.executeInUnitOfWork(jobsConfiguration.unitOfWorkManager(), () -> { + org.kie.kogito.process.ProcessInstance pi = process.createInstance(process.createModel()); + if (pi != null) { + pi.start(TRIGGER, null); + } + return null; + }); + limit--; + if (limit == 0) { + jobService.cancelJob(id); + } + LOGGER.debug("Job {} completed", id); + } finally { + if (removeAtExecution) { + jobService.cancelJob(id); + } + } + } +} \ No newline at end of file diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryUserTaskJobExecutorFactory.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryUserTaskJobExecutorFactory.java new file mode 100644 index 00000000000..1381928eba9 --- /dev/null +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/InMemoryUserTaskJobExecutorFactory.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.services.jobs.impl; + +import java.util.Optional; +import java.util.Set; + +import org.kie.kogito.jobs.JobDescription; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.jobs.descriptors.UserTaskInstanceJobDescription; +import org.kie.kogito.process.ProcessInstanceOptimisticLockingException; +import org.kie.kogito.usertask.UserTaskInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.kie.kogito.services.uow.UnitOfWorkExecutor.executeInUnitOfWork; + +public class InMemoryUserTaskJobExecutorFactory implements JobExecutorFactory { + + private InMemoryJobContext jobsConfiguration; + + public InMemoryUserTaskJobExecutorFactory(InMemoryJobContext jobsConfiguration) { + this.jobsConfiguration = jobsConfiguration; + } + + @Override + public Set> types() { + return Set.of(UserTaskInstanceJobDescription.class); + } + + @Override + public Runnable createNewRunnable(JobsService jobService, JobDescription jobDescription) { + if (jobDescription instanceof UserTaskInstanceJobDescription userTaskInstanceJobDescription) { + return userTaskJobDescription(jobService, jobsConfiguration, userTaskInstanceJobDescription); + } + throw new IllegalArgumentException("single job description not supported for " + jobDescription); + } + + @Override + public Runnable createNewRepeteableRunnable(JobsService jobService, JobDescription jobDescription) { + if (jobDescription instanceof UserTaskInstanceJobDescription userTaskInstanceJobDescription) { + return repetableUserTaskJobDescription(jobService, jobsConfiguration, userTaskInstanceJobDescription); + } + throw new IllegalArgumentException("repeteable job description not supported for " + jobDescription); + } + + private Runnable userTaskJobDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, UserTaskInstanceJobDescription userTaskInstanceJobDescription) { + return new SignalUserTaskInstanceOnExpiredTimer(jobService, jobsConfiguration, userTaskInstanceJobDescription, true, -1); + } + + private Runnable repetableUserTaskJobDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, UserTaskInstanceJobDescription userTaskInstanceJobDescription) { + return new SignalUserTaskInstanceOnExpiredTimer(jobService, jobsConfiguration, userTaskInstanceJobDescription, false, userTaskInstanceJobDescription.expirationTime().repeatLimit()); + } + +} + +class SignalUserTaskInstanceOnExpiredTimer implements Runnable { + + private static Logger LOGGER = LoggerFactory.getLogger(SignalUserTaskInstanceOnExpiredTimer.class); + + private boolean removeAtExecution; + private Integer limit; + private JobsService jobService; + + private InMemoryJobContext jobsConfiguration; + + private UserTaskInstanceJobDescription userTaskInstanceJobDescription; + + public SignalUserTaskInstanceOnExpiredTimer(JobsService jobService, InMemoryJobContext jobsConfiguration, UserTaskInstanceJobDescription userTaskInstanceJobDescription, boolean removeAtExecution, + Integer limit) { + this.userTaskInstanceJobDescription = userTaskInstanceJobDescription; + this.removeAtExecution = removeAtExecution; + this.limit = limit; + this.jobsConfiguration = jobsConfiguration; + this.jobService = jobService; + } + + @Override + public void run() { + String jobId = userTaskInstanceJobDescription.id(); + String userTaskInstanceId = userTaskInstanceJobDescription.getUserTaskInstanceId(); + try { + Optional userTaskInstance = jobsConfiguration.userTasks().instances().findById(userTaskInstanceId); + if (userTaskInstance.isEmpty()) { + LOGGER.info("Skipping Job {}. There is no user task instance of id {} ", jobId, userTaskInstanceId); + return; + } + limit--; + executeInUnitOfWork(jobsConfiguration.unitOfWorkManager(), () -> { + userTaskInstance.get().trigger(userTaskInstanceJobDescription); + return null; + }); + if (limit == 0) { + jobService.cancelJob(jobId); + } + LOGGER.debug("Job {} completed", jobId); + } catch (ProcessInstanceOptimisticLockingException ex) { + LOGGER.info("Retrying Job {} due to: {}", jobId, ex.getMessage()); + limit++; + run(); + } finally { + if (removeAtExecution) { + jobService.cancelJob(jobId); + } + } + } +} diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/JobExecutorFactory.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/JobExecutorFactory.java new file mode 100644 index 00000000000..f689e125771 --- /dev/null +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/JobExecutorFactory.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.services.jobs.impl; + +import java.util.Set; + +import org.kie.kogito.jobs.JobDescription; +import org.kie.kogito.jobs.JobsService; + +public interface JobExecutorFactory { + + Set> types(); + + default boolean accept(JobDescription jobDescription) { + return types().contains(jobDescription.getClass()); + } + + Runnable createNewRunnable(JobsService jobService, JobDescription jobDescription); + + Runnable createNewRepeteableRunnable(JobsService jobService, JobDescription jobDescription); + +} diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/LegacyInMemoryJobExecutorFactory.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/LegacyInMemoryJobExecutorFactory.java new file mode 100644 index 00000000000..0700b59528f --- /dev/null +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/LegacyInMemoryJobExecutorFactory.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.services.jobs.impl; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import org.kie.api.runtime.process.ProcessInstance; +import org.kie.kogito.internal.process.runtime.KogitoProcessInstance; +import org.kie.kogito.jobs.JobDescription; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessJobDescription; +import org.kie.kogito.services.uow.UnitOfWorkExecutor; +import org.kie.kogito.timer.TimerInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.kie.kogito.services.jobs.impl.TriggerJobCommand.SIGNAL; + +public class LegacyInMemoryJobExecutorFactory implements JobExecutorFactory { + + private static final String TRIGGER = "timer"; + + private static Logger LOGGER = LoggerFactory.getLogger(LegacyInMemoryJobExecutorFactory.class); + + private InMemoryJobContext jobsConfiguration; + + public LegacyInMemoryJobExecutorFactory(InMemoryJobContext jobsConfiguration) { + this.jobsConfiguration = jobsConfiguration; + } + + @Override + public Set> types() { + return Set.of(ProcessInstanceJobDescription.class, ProcessJobDescription.class); + } + + @Override + public Runnable createNewRunnable(JobsService jobService, JobDescription jobDescription) { + if (jobDescription instanceof ProcessInstanceJobDescription processInstanceJobDescription) { + return processInstanceJobDescription(jobService, jobsConfiguration, processInstanceJobDescription, true, 1); + } else if (jobDescription instanceof ProcessJobDescription processJobDescription) { + return processJobByDescription(jobService, jobsConfiguration, processJobDescription); + } + throw new IllegalArgumentException("single job description not supported for " + jobDescription); + } + + @Override + public Runnable createNewRepeteableRunnable(JobsService jobService, JobDescription jobDescription) { + if (jobDescription instanceof ProcessInstanceJobDescription processInstanceJobDescription) { + return processInstanceJobDescription(jobService, jobsConfiguration, processInstanceJobDescription, false, processInstanceJobDescription.expirationTime().repeatLimit()); + } else if (jobDescription instanceof ProcessJobDescription processJobDescription) { + return repeatableJobByDescription(jobService, jobsConfiguration, processJobDescription); + } + throw new IllegalArgumentException("multiple job description not supported for " + jobDescription); + } + + protected Runnable processInstanceJobDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, ProcessInstanceJobDescription description, boolean remove, int limit) { + String id = description.id(); + AtomicInteger counter = new AtomicInteger(limit); + return () -> { + try { + UnitOfWorkExecutor.executeInUnitOfWork(jobsConfiguration.unitOfWorkManager(), () -> { + ProcessInstance pi = jobsConfiguration.runtime().getProcessInstance(description.processInstanceId()); + if (pi != null) { + pi.signalEvent(SIGNAL, TimerInstance.with(description.id(), description.timerId(), counter.decrementAndGet())); + if (counter.get() == 0) { + jobService.cancelJob(id); + } + } else { + // since owning process instance does not exist cancel timers + jobService.cancelJob(id); + } + return null; + }); + LOGGER.debug("Job {} completed", id); + } finally { + if (remove) { + jobService.cancelJob(id); + } + } + }; + } + + protected Runnable processJobByDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, ProcessJobDescription description) { + return processCommand(jobService, jobsConfiguration, description, true); + } + + protected Runnable repeatableJobByDescription(JobsService jobService, InMemoryJobContext jobsConfiguration, ProcessJobDescription description) { + return processCommand(jobService, jobsConfiguration, description, false); + } + + private Runnable processCommand(JobsService jobService, InMemoryJobContext jobsConfiguration, ProcessJobDescription description, boolean remove) { + + String id = description.id(); + AtomicInteger counter = new AtomicInteger(description.expirationTime().repeatLimit()); + String processId = description.processId(); + return () -> { + try { + LOGGER.debug("Job {} started", id); + UnitOfWorkExecutor.executeInUnitOfWork(jobsConfiguration.unitOfWorkManager(), () -> { + KogitoProcessInstance pi = jobsConfiguration.runtime().createProcessInstance(processId, null); + if (pi != null) { + jobsConfiguration.runtime().startProcessInstance(pi.getStringId(), TRIGGER); + } + return null; + }); + if (counter.decrementAndGet() == 0) { + jobService.cancelJob(id); + } + LOGGER.debug("Job {} completed", id); + } finally { + if (remove) { + jobService.cancelJob(id); + } + } + }; + } + +} diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/LegacyInMemoryJobService.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/LegacyInMemoryJobService.java index 9ef2adcf861..960b96624b3 100644 --- a/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/LegacyInMemoryJobService.java +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/LegacyInMemoryJobService.java @@ -18,92 +18,13 @@ */ package org.kie.kogito.services.jobs.impl; -import java.util.concurrent.atomic.AtomicInteger; - -import org.kie.api.runtime.process.ProcessInstance; -import org.kie.kogito.internal.process.runtime.KogitoProcessInstance; import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; -import org.kie.kogito.jobs.ProcessJobDescription; -import org.kie.kogito.services.uow.UnitOfWorkExecutor; -import org.kie.kogito.timer.TimerInstance; import org.kie.kogito.uow.UnitOfWorkManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.kie.kogito.services.jobs.impl.TriggerJobCommand.SIGNAL; public class LegacyInMemoryJobService extends InMemoryJobService { - private static final Logger LOGGER = LoggerFactory.getLogger(LegacyInMemoryJobService.class); - private KogitoProcessRuntime processRuntime; - - public LegacyInMemoryJobService(KogitoProcessRuntime processRuntime, UnitOfWorkManager unitOfWorkManager) { - super(null, unitOfWorkManager); - this.processRuntime = processRuntime; - } - - @Override - public Runnable getSignalProcessInstanceCommand(ProcessInstanceJobDescription description, boolean remove, int limit) { - String id = description.id(); - AtomicInteger counter = new AtomicInteger(limit); - return () -> { - try { - UnitOfWorkExecutor.executeInUnitOfWork(unitOfWorkManager, () -> { - ProcessInstance pi = processRuntime.getProcessInstance(description.processInstanceId()); - if (pi != null) { - pi.signalEvent(SIGNAL, TimerInstance.with(description.id(), description.timerId(), counter.decrementAndGet())); - if (counter.get() == 0) { - cancelJob(id, false); - } - } else { - // since owning process instance does not exist cancel timers - cancelJob(id, false); - } - return null; - }); - LOGGER.debug("Job {} completed", id); - } finally { - if (remove) { - cancelJob(id); - } - } - }; + public LegacyInMemoryJobService(KogitoProcessRuntime runtime, UnitOfWorkManager unitOfWorkManager) { + registerJobExecutorFactory(new LegacyInMemoryJobExecutorFactory(new InMemoryJobContext(runtime, unitOfWorkManager, null, null))); } - @Override - protected Runnable processJobByDescription(ProcessJobDescription description) { - return processCommand(description, true); - } - - private Runnable processCommand(ProcessJobDescription description, boolean remove) { - String id = description.id(); - AtomicInteger counter = new AtomicInteger(description.expirationTime().repeatLimit()); - String processId = description.processId(); - return () -> { - try { - LOGGER.debug("Job {} started", id); - UnitOfWorkExecutor.executeInUnitOfWork(unitOfWorkManager, () -> { - KogitoProcessInstance pi = processRuntime.createProcessInstance(processId, null); - if (pi != null) { - processRuntime.startProcessInstance(pi.getStringId(), TRIGGER); - } - return null; - }); - if (counter.decrementAndGet() == 0) { - cancelJob(id, false); - } - LOGGER.debug("Job {} completed", id); - } finally { - if (remove) { - cancelJob(id); - } - } - }; - } - - @Override - protected Runnable repeatableProcessJobByDescription(ProcessJobDescription description) { - return processCommand(description, false); - } } diff --git a/addons/common/flyway/src/test/java/org/kie/flyway/integration/TestKieFlywayNamedModule.java b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/StaticJobService.java similarity index 72% rename from addons/common/flyway/src/test/java/org/kie/flyway/integration/TestKieFlywayNamedModule.java rename to api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/StaticJobService.java index 8eea404acc1..94c8027a345 100644 --- a/addons/common/flyway/src/test/java/org/kie/flyway/integration/TestKieFlywayNamedModule.java +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/jobs/impl/StaticJobService.java @@ -16,19 +16,16 @@ * specific language governing permissions and limitations * under the License. */ +package org.kie.kogito.services.jobs.impl; -package org.kie.flyway.integration; +public class StaticJobService { -public class TestKieFlywayNamedModule implements KieFlywayNamedModule { + private static InMemoryJobService INSTANCE; - private boolean enabled; - - public TestKieFlywayNamedModule(boolean enabled) { - this.enabled = enabled; - } - - @Override - public boolean isEnabled() { - return enabled; + public static InMemoryJobService staticJobService() { + if (INSTANCE == null) { + INSTANCE = new InMemoryJobService(); + } + return INSTANCE; } } diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/uow/StaticUnitOfWorkManger.java b/api/kogito-services/src/main/java/org/kie/kogito/services/uow/StaticUnitOfWorkManger.java new file mode 100644 index 00000000000..0d4b4b9665f --- /dev/null +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/uow/StaticUnitOfWorkManger.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.services.uow; + +import org.kie.kogito.uow.UnitOfWorkManager; + +public class StaticUnitOfWorkManger { + + private static final UnitOfWorkManager SINGLETON = new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory()); + + public static UnitOfWorkManager staticUnitOfWorkManager() { + return SINGLETON; + } +} diff --git a/api/kogito-services/src/main/java/org/kie/kogito/services/uow/UnitOfWorkExecutor.java b/api/kogito-services/src/main/java/org/kie/kogito/services/uow/UnitOfWorkExecutor.java index b9a81aea334..12f792fbeef 100644 --- a/api/kogito-services/src/main/java/org/kie/kogito/services/uow/UnitOfWorkExecutor.java +++ b/api/kogito-services/src/main/java/org/kie/kogito/services/uow/UnitOfWorkExecutor.java @@ -43,7 +43,6 @@ public static T executeInUnitOfWork(UnitOfWorkManager uowManager, Supplier readAssignments(Element parent, Function { @@ -127,7 +127,7 @@ public static List from(Resource... resource) { } public static List from(WorkItemHandlerConfig config, Resource... resource) { - return from(new StaticProcessConfig(config, new DefaultProcessEventListenerConfig(), new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory())), resource); + return from(new StaticProcessConfig(config, new DefaultProcessEventListenerConfig(), staticUnitOfWorkManager()), resource); } public static List from(ProcessConfig config, Resource... resources) { diff --git a/jbpm/jbpm-flow/pom.xml b/jbpm/jbpm-flow/pom.xml index d0fc15edb26..0da528e8e28 100755 --- a/jbpm/jbpm-flow/pom.xml +++ b/jbpm/jbpm-flow/pom.xml @@ -152,6 +152,11 @@ assertj-core test + + org.awaitility + awaitility + test + diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java index cb388bb3610..2223cd5687d 100644 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java @@ -42,7 +42,7 @@ import org.kie.kogito.jobs.ExactExpirationTime; import org.kie.kogito.jobs.ExpirationTime; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessJobDescription; import org.kie.kogito.signal.SignalManager; import static org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_ENVIRONMENT_KEY; @@ -100,7 +100,7 @@ protected void initStartTimers(Collection processes, InternalKnowledgeR if (startNodes != null && !startNodes.isEmpty()) { for (StartNode startNode : startNodes) { if (startNode != null && startNode.getTimer() != null) { - jobService.scheduleProcessJob(ProcessJobDescription.of(createTimerInstance(startNode.getTimer(), kruntime), p.getId())); + jobService.scheduleJob(ProcessJobDescription.of(createTimerInstance(startNode.getTimer(), kruntime), p.getId())); } } } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java index a972d72daf2..7c1a43e5c86 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java @@ -51,8 +51,6 @@ import org.kie.kogito.internal.process.runtime.KogitoProcessInstance; import org.kie.kogito.internal.process.workitem.KogitoWorkItemManager; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.process.Processes; -import org.kie.kogito.services.jobs.impl.InMemoryJobService; import org.kie.kogito.signal.SignalManager; import org.kie.kogito.uow.UnitOfWorkManager; @@ -81,7 +79,7 @@ protected LightProcessRuntime(ProcessRuntimeContext runtimeContext, ProcessRunti this.runtimeContext = runtimeContext; this.processInstanceManager = services.getProcessInstanceManager(); this.signalManager = services.getSignalManager(); - this.jobService = services.getJobsService() == null ? InMemoryJobService.get(application.get(Processes.class), this.unitOfWorkManager) : services.getJobsService(); + this.jobService = services.getJobsService(); this.processEventSupport = services.getEventSupport(); this.workItemManager = services.getKogitoWorkItemManager(); if (isActive()) { @@ -144,9 +142,7 @@ public KogitoProcessInstance startProcess(String processId, CorrelationKey corre @Override public KogitoProcessInstance createProcessInstance(String processId, CorrelationKey correlationKey, Map parameters) { return createProcessInstance( - runtimeContext.findProcess(processId) - .orElseThrow(() -> new IllegalArgumentException( - "Unknown process ID: " + processId)), + runtimeContext.findProcess(processId).orElseThrow(() -> new IllegalArgumentException("Unknown process ID: " + processId)), correlationKey, parameters); } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/AsyncEventNodeInstance.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/AsyncEventNodeInstance.java index f96f9476ad3..59cc4263c8b 100644 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/AsyncEventNodeInstance.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/AsyncEventNodeInstance.java @@ -33,7 +33,7 @@ import org.kie.kogito.jobs.ExactExpirationTime; import org.kie.kogito.jobs.ExpirationTime; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.process.ProcessInstance; import org.kie.kogito.services.uow.BaseWorkUnit; import org.kie.kogito.timer.TimerInstance; @@ -99,7 +99,7 @@ public void internalTrigger(KogitoNodeInstance from, String type) { new BaseWorkUnit<>(this, instance -> { ExpirationTime expirationTime = ExactExpirationTime.of(ZonedDateTime.now().plus(1, ChronoUnit.MILLIS)); ProcessInstanceJobDescription jobDescription = - ProcessInstanceJobDescription.builder() + ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .id(getJobId()) .timerId("-1") .expirationTime(expirationTime) @@ -110,7 +110,7 @@ public void internalTrigger(KogitoNodeInstance from, String type) { .nodeInstanceId(Optional.ofNullable(from).map(KogitoNodeInstance::getStringId).orElse(null)) .build(); JobsService jobService = processRuntime.getJobsService(); - String jobId = jobService.scheduleProcessInstanceJob(jobDescription); + String jobId = jobService.scheduleJob(jobDescription); setJobId(jobId); }, i -> { }, WorkUnit.LOW_PRIORITY)); diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/HumanTaskNode.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/HumanTaskNode.java index 872276850ae..1632732093b 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/HumanTaskNode.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/HumanTaskNode.java @@ -38,9 +38,9 @@ public class HumanTaskNode extends WorkItemNode { "TaskName", "NodeName", "NotStartedNotify", + "NotStartedReassign", "NotCompletedNotify", "NotCompletedReassign", - "NotStartedReassign", "Description", "Comment", "ActorId", diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java index fd78e89418f..b7e6e07307b 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java @@ -714,9 +714,8 @@ protected Object resolveValue(String expression, Function, O Object variableValue = MVELProcessHelper.evaluator().eval(paramName, new NodeInstanceResolverFactory(this)); replacements.put(paramName, variableValue); } catch (Exception t) { - logger.error("Could not find variable scope for variable {}", paramName); - logger.error("when trying to replace variable in processId for node {}", getNodeName()); - logger.error("Continuing without setting process id."); + logger.error("MVEL failed to replace variable {} in process {} for node {}. Continuing without setting process id", paramName, processInstance.getProcessId(), + getNodeName(), t); } } } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java index e185f6d0197..aa68a09c331 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java @@ -95,7 +95,7 @@ import org.kie.kogito.internal.process.runtime.MessageException; import org.kie.kogito.jobs.DurationExpirationTime; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.process.BaseEventDescription; import org.kie.kogito.process.EventDescription; import org.kie.kogito.process.NamedDataType; @@ -442,8 +442,6 @@ public void setState(final int state, String outcome) { removeEventListeners(); processRuntime.getProcessInstanceManager().removeProcessInstance(this); - processRuntime.getProcessEventSupport().fireAfterProcessCompleted(this, kruntime); - if (isSignalCompletion()) { List listeners = eventListeners.get("processInstanceCompleted:" + getStringId()); @@ -455,6 +453,8 @@ public void setState(final int state, String outcome) { processRuntime.getSignalManager().signalEvent("processInstanceCompleted:" + getStringId(), this); } + processRuntime.getProcessEventSupport().fireAfterProcessCompleted(this, kruntime); + } else { super.setState(state, outcome); } @@ -599,7 +599,7 @@ private TimerInstance createDurationTimer(long duration) { private TimerInstance registerTimer(TimerInstance timerInstance) { ProcessInstanceJobDescription description = - ProcessInstanceJobDescription.builder() + ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .id(timerInstance.getId()) .timerId(timerInstance.getTimerId()) .expirationTime(DurationExpirationTime.after(timerInstance.getDelay())) @@ -607,7 +607,7 @@ private TimerInstance registerTimer(TimerInstance timerInstance) { .processId(getProcessId()) .build(); JobsService jobsService = InternalProcessRuntime.asKogitoProcessRuntime(getKnowledgeRuntime().getProcessRuntime()).getJobsService(); - jobsService.scheduleProcessInstanceJob(description); + jobsService.scheduleJob(description); return timerInstance; } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java index 2b0517718c6..c03ce5c4377 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java @@ -52,7 +52,7 @@ import org.kie.kogito.jobs.ExactExpirationTime; import org.kie.kogito.jobs.ExpirationTime; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.process.expr.Expression; import org.kie.kogito.process.expr.ExpressionHandlerFactory; import org.kie.kogito.timer.TimerInstance; @@ -95,7 +95,7 @@ public void internalTrigger(KogitoNodeInstance from, String type) { JobsService jobService = ((KogitoProcessRuntime.Provider) getProcessInstance().getKnowledgeRuntime().getProcessRuntime()).getKogitoProcessRuntime().getJobsService(); for (Timer timer : timers.keySet()) { ProcessInstanceJobDescription jobDescription = - ProcessInstanceJobDescription.builder() + ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .generateId() .timerId(Long.toString(timer.getId())) .expirationTime(createTimerInstance(timer)) @@ -105,7 +105,7 @@ public void internalTrigger(KogitoNodeInstance from, String type) { .processInstanceId(getProcessInstance().getStringId()) .nodeInstanceId(this.getId()) .build(); - String jobId = jobService.scheduleProcessInstanceJob(jobDescription); + String jobId = jobService.scheduleJob(jobDescription); timerInstances.add(jobId); timerInstancesReference.put(jobId, Long.toString(timer.getId())); } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/TimerNodeInstance.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/TimerNodeInstance.java index 452e9785287..5ca0c76a289 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/TimerNodeInstance.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/TimerNodeInstance.java @@ -36,7 +36,7 @@ import org.kie.kogito.internal.process.workitem.WorkItemExecutionException; import org.kie.kogito.jobs.ExpirationTime; import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.process.BaseEventDescription; import org.kie.kogito.process.EventDescription; import org.kie.kogito.services.uow.BaseWorkUnit; @@ -82,7 +82,7 @@ public void internalTrigger(KogitoNodeInstance from, String type) { processRuntime.getUnitOfWorkManager().currentUnitOfWork().intercept( new BaseWorkUnit<>(this, instance -> { ProcessInstanceJobDescription jobDescription = - ProcessInstanceJobDescription.builder() + ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .id(getTimerId()) .timerId("-1") .expirationTime(expirationTime) @@ -93,7 +93,7 @@ public void internalTrigger(KogitoNodeInstance from, String type) { .nodeInstanceId(this.getId()) .build(); JobsService jobService = processRuntime.getJobsService(); - String jobId = jobService.scheduleProcessInstanceJob(jobDescription); + String jobId = jobService.scheduleJob(jobDescription); internalSetTimerId(jobId); }, i -> { }, WorkUnit.LOW_PRIORITY)); diff --git a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcess.java b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcess.java index 96ec9a0e659..89e3193c793 100644 --- a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcess.java +++ b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcess.java @@ -57,7 +57,7 @@ import org.kie.kogito.jobs.DurationExpirationTime; import org.kie.kogito.jobs.ExactExpirationTime; import org.kie.kogito.jobs.ExpirationTime; -import org.kie.kogito.jobs.ProcessJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessJobDescription; import org.kie.kogito.process.MutableProcessInstances; import org.kie.kogito.process.Process; import org.kie.kogito.process.ProcessConfig; @@ -227,7 +227,7 @@ public void activate() { if (startNodes != null && !startNodes.isEmpty()) { for (StartNode startNode : startNodes) { if (startNode != null && startNode.getTimer() != null) { - String timerId = processRuntime.getJobsService().scheduleProcessJob(ProcessJobDescription.of(configureTimerInstance(startNode.getTimer()), this)); + String timerId = processRuntime.getJobsService().scheduleJob(ProcessJobDescription.of(configureTimerInstance(startNode.getTimer()), this)); startTimerInstances.add(timerId); } } diff --git a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java index ac13e659424..826efc66d4f 100644 --- a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java +++ b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java @@ -36,6 +36,7 @@ import org.kie.kogito.process.ProcessVersionResolver; import org.kie.kogito.process.WorkItemHandlerConfig; import org.kie.kogito.services.identity.NoOpIdentityProvider; +import org.kie.kogito.services.jobs.impl.StaticJobService; import org.kie.kogito.services.signal.DefaultSignalManagerHub; import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; @@ -69,10 +70,8 @@ protected AbstractProcessConfig( this.workItemHandlerConfig = mergeWorkItemHandler(workItemHandlerConfig, DefaultWorkItemHandlerConfig::new); this.processEventListenerConfig = merge(processEventListenerConfigs, processEventListeners); - this.unitOfWorkManager = orDefault(unitOfWorkManager, - () -> new DefaultUnitOfWorkManager( - new CollectingUnitOfWorkFactory())); - this.jobsService = orDefault(jobsService, () -> null); + this.unitOfWorkManager = orDefault(unitOfWorkManager, () -> new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory())); + this.jobsService = orDefault(jobsService, StaticJobService::staticJobService); this.versionResolver = orDefault(versionResolver, () -> null); this.identityProvider = orDefault(identityProvider, NoOpIdentityProvider::new); this.businessCalendar = orDefault(businessCalendar, () -> null); diff --git a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java index 343f232a042..55f688757b1 100644 --- a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java +++ b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java @@ -27,11 +27,12 @@ import org.kie.kogito.process.WorkItemHandlerConfig; import org.kie.kogito.services.identity.NoOpIdentityProvider; import org.kie.kogito.services.signal.DefaultSignalManagerHub; -import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; -import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; import org.kie.kogito.signal.SignalManagerHub; import org.kie.kogito.uow.UnitOfWorkManager; +import static org.kie.kogito.services.jobs.impl.StaticJobService.staticJobService; +import static org.kie.kogito.services.uow.StaticUnitOfWorkManger.staticUnitOfWorkManager; + public class StaticProcessConfig implements ProcessConfig { private final WorkItemHandlerConfig workItemHandlerConfig; @@ -44,11 +45,21 @@ public class StaticProcessConfig implements ProcessConfig { private final IdentityProvider identityProvider; private final BusinessCalendar businessCalendar; + public StaticProcessConfig(JobsService jobService) { + this(new DefaultWorkItemHandlerConfig(), + new DefaultProcessEventListenerConfig(), + staticUnitOfWorkManager(), + jobService, + null, + new NoOpIdentityProvider(), + null); + } + public StaticProcessConfig( WorkItemHandlerConfig workItemHandlerConfig, ProcessEventListenerConfig processEventListenerConfig, UnitOfWorkManager unitOfWorkManager) { - this(workItemHandlerConfig, processEventListenerConfig, unitOfWorkManager, null, null, new NoOpIdentityProvider(), null); + this(workItemHandlerConfig, processEventListenerConfig, unitOfWorkManager, staticJobService(), null, new NoOpIdentityProvider(), null); } public StaticProcessConfig( @@ -72,8 +83,8 @@ public StaticProcessConfig( public StaticProcessConfig() { this(new DefaultWorkItemHandlerConfig(), new DefaultProcessEventListenerConfig(), - new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory()), - null, + staticUnitOfWorkManager(), + staticJobService(), null, new NoOpIdentityProvider(), null); diff --git a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/TimerTest.java b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/TimerTest.java index 67f835b37ad..93f0279d81e 100755 --- a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/TimerTest.java +++ b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/TimerTest.java @@ -18,6 +18,7 @@ */ package org.jbpm.process; +import java.time.Duration; import java.util.concurrent.TimeUnit; import org.drools.core.common.InternalWorkingMemory; @@ -26,21 +27,19 @@ import org.jbpm.process.instance.ProcessRuntimeFactoryServiceImpl; import org.jbpm.ruleflow.instance.RuleFlowProcessInstance; import org.jbpm.test.util.AbstractBaseTest; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime; import org.kie.kogito.jobs.DurationExpirationTime; import org.kie.kogito.jobs.ExactExpirationTime; -import org.kie.kogito.jobs.JobsService; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.services.jobs.impl.LegacyInMemoryJobService; -import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; -import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; +import org.kie.kogito.services.uow.StaticUnitOfWorkManger; import org.kie.kogito.timer.TimerInstance; import org.slf4j.LoggerFactory; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; import static org.jbpm.workflow.instance.node.TimerNodeInstance.TIMER_TRIGGERED_EVENT; public class TimerTest extends AbstractBaseTest { @@ -57,95 +56,75 @@ public void addLogger() { @Test @Timeout(value = 10L, unit = TimeUnit.SECONDS) - @Disabled - public void testTimer() { + public void testTimer() throws Exception { KogitoProcessRuntime kruntime = createKogitoProcessRuntime(); - - RuleFlowProcessInstance processInstance = new RuleFlowProcessInstance() { - private static final long serialVersionUID = 510l; - - public void signalEvent(String type, Object event) { - if (TIMER_TRIGGERED_EVENT.equals(type)) { - TimerInstance timer = (TimerInstance) event; - logger.info("Timer {} triggered", timer.getId()); - counter++; + try (LegacyInMemoryJobService jobService = new LegacyInMemoryJobService(kruntime, StaticUnitOfWorkManger.staticUnitOfWorkManager());) { + RuleFlowProcessInstance processInstance = new RuleFlowProcessInstance() { + private static final long serialVersionUID = 510l; + + public void signalEvent(String type, Object event) { + if (TIMER_TRIGGERED_EVENT.equals(type)) { + TimerInstance timer = (TimerInstance) event; + logger.info("Timer {} triggered", timer.getId()); + counter++; + } } - } - }; - processInstance.setKnowledgeRuntime(((InternalWorkingMemory) kruntime.getKieSession()).getKnowledgeRuntime()); - processInstance.setId("1234"); - InternalProcessRuntime processRuntime = ((InternalProcessRuntime) ((InternalWorkingMemory) kruntime.getKieSession()).getProcessRuntime()); - processRuntime.getProcessInstanceManager().internalAddProcessInstance(processInstance); - - new Thread(() -> kruntime.getKieSession().fireUntilHalt()).start(); - JobsService jobService = new LegacyInMemoryJobService(kruntime, new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory())); - - ProcessInstanceJobDescription desc = ProcessInstanceJobDescription.builder() - .expirationTime(ExactExpirationTime.now()) - .processInstanceId(processInstance.getStringId()) - .processId("test") - .id("job1") - .timerId("timer1") - .build(); - jobService.scheduleProcessInstanceJob(desc); - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // do nothing - } - assertThat(counter).isEqualTo(1); - - counter = 0; - desc = ProcessInstanceJobDescription.builder() - .expirationTime(DurationExpirationTime.after(500)) - .processInstanceId(processInstance.getStringId()) - .processId("test") - .id("job2") - .timerId("timer2") - .build(); - jobService.scheduleProcessInstanceJob(desc); - assertThat(counter).isZero(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // do nothing - } - assertThat(counter).isEqualTo(1); - - counter = 0; - desc = ProcessInstanceJobDescription.builder() - .expirationTime(DurationExpirationTime.repeat(500, 300L)) - .processInstanceId(processInstance.getStringId()) - .processId("test") - .id("job3") - .timerId("timer3") - .build(); - String jobId = jobService.scheduleProcessInstanceJob(desc); - assertThat(counter).isZero(); - try { - Thread.sleep(700); - } catch (InterruptedException e) { - // do nothing - } - assertThat(counter).isEqualTo(1); + }; + processInstance.setKnowledgeRuntime(((InternalWorkingMemory) kruntime.getKieSession()).getKnowledgeRuntime()); + processInstance.setId("1234"); + InternalProcessRuntime processRuntime = ((InternalProcessRuntime) ((InternalWorkingMemory) kruntime.getKieSession()).getProcessRuntime()); + processRuntime.getProcessInstanceManager().internalAddProcessInstance(processInstance); + + new Thread(() -> kruntime.getKieSession().fireUntilHalt()).start(); + + ProcessInstanceJobDescription desc = ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() + .expirationTime(ExactExpirationTime.now()) + .processInstanceId(processInstance.getStringId()) + .processId("test") + .id("job1") + .timerId("timer1") + .build(); + jobService.scheduleJob(desc); + + await().atMost(Duration.ofSeconds(5L)).until(() -> counter == 1); + assertThat(counter).isEqualTo(1); + + counter = 0; + desc = ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() + .expirationTime(DurationExpirationTime.after(500)) + .processInstanceId(processInstance.getStringId()) + .processId("test") + .id("job2") + .timerId("timer2") + .build(); + jobService.scheduleJob(desc); + assertThat(counter).isZero(); + await().atMost(Duration.ofSeconds(5L)).until(() -> counter == 1); + assertThat(counter).isEqualTo(1); + + counter = 0; + desc = ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() + .expirationTime(DurationExpirationTime.repeat(500, 300L)) + .processInstanceId(processInstance.getStringId()) + .processId("test") + .id("job3") + .timerId("timer3") + .build(); + String jobId = jobService.scheduleJob(desc); + assertThat(counter).isZero(); + + await().atMost(Duration.ofSeconds(5L)).until(() -> counter == 1); + assertThat(counter).isEqualTo(1); + + await().atMost(Duration.ofSeconds(5L)).until(() -> counter == 4); + assertThat(counter).isEqualTo(4); + + jobService.cancelJob(jobId); + int lastCount = counter; + // value is preserved + await().during(Duration.ofSeconds(2L)).until(() -> counter == lastCount); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // do nothing - } - // we can't know exactly how many times this will fire as timers are not precise, but should be at least 4 - assertThat(counter >= 4).isTrue(); - - jobService.cancelJob(jobId); - int lastCount = counter; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // do nothing } - assertThat(counter).isEqualTo(lastCount); } } diff --git a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/StandaloneBPMNProcessTest.java b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/StandaloneBPMNProcessTest.java index de8df91a30c..e77cbfa99a8 100755 --- a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/StandaloneBPMNProcessTest.java +++ b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/StandaloneBPMNProcessTest.java @@ -400,8 +400,7 @@ public void testEventBasedSplit2() throws Exception { org.kie.kogito.process.ProcessInstance instanceTimer = processDefinition.createInstance(modelTimer); instanceTimer.start(); assertThat(instanceTimer.status()).isEqualTo(org.kie.kogito.process.ProcessInstance.STATE_ACTIVE); - assertThat(countDownListener.waitTillCompleted(15000)).isTrue(); - assertThat(instanceYes.status()).isEqualTo(org.kie.kogito.process.ProcessInstance.STATE_COMPLETED); + assertThat(countDownListener.waitTillCompleted()).isTrue(); assertThat(instanceTimer.status()).isEqualTo(org.kie.kogito.process.ProcessInstance.STATE_COMPLETED); } diff --git a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java index c89cc7a824d..0ca0a9bde87 100644 --- a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java +++ b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java @@ -32,12 +32,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.kie.kogito.Application; -import org.kie.kogito.StaticApplication; -import org.kie.kogito.StaticConfig; import org.kie.kogito.calendar.BusinessCalendar; -import org.kie.kogito.process.ProcessConfig; import org.kie.kogito.process.ProcessInstance; -import org.kie.kogito.process.bpmn2.BpmnProcesses; import org.kie.kogito.process.impl.AbstractProcessConfig; import static org.assertj.core.api.Assertions.assertThat; @@ -55,9 +51,7 @@ public static void createCalendars() { @Test public void testTimerWithWorkingDayCalendar() throws InterruptedException { - BpmnProcesses bpmnProcesses = new BpmnProcesses(); - ProcessConfig config = new MockProcessConfig(workingDayCalendar); - Application app = new StaticApplication(new StaticConfig(null, config), bpmnProcesses); + Application app = ProcessTestHelper.newApplication(new MockProcessConfig(workingDayCalendar)); TestWorkItemHandler workItemHandler = new TestWorkItemHandler(); ProcessTestHelper.registerHandler(app, "Human Task", workItemHandler); org.kie.kogito.process.Process processDefinition = BusinessCalendarTimerProcess.newProcess(app); @@ -71,9 +65,7 @@ public void testTimerWithWorkingDayCalendar() throws InterruptedException { @Test public void testTimerWithNotWorkingDayCalendar() throws InterruptedException { - BpmnProcesses bpmnProcesses = new BpmnProcesses(); - ProcessConfig config = new MockProcessConfig(notWorkingDayCalendar); - Application app = new StaticApplication(new StaticConfig(null, config), bpmnProcesses); + Application app = ProcessTestHelper.newApplication(new MockProcessConfig(notWorkingDayCalendar)); TestWorkItemHandler workItemHandler = new TestWorkItemHandler(); ProcessTestHelper.registerHandler(app, "Human Task", workItemHandler); org.kie.kogito.process.Process processDefinition = BusinessCalendarTimerProcess.newProcess(app); diff --git a/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java b/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java index 1c111d88d90..9da2d64e30f 100644 --- a/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java +++ b/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java @@ -30,6 +30,7 @@ import org.kie.api.event.process.ProcessNodeEvent; import org.kie.api.event.process.ProcessNodeLeftEvent; import org.kie.api.event.process.ProcessNodeTriggeredEvent; +import org.kie.kogito.Addons; import org.kie.kogito.Application; import org.kie.kogito.Model; import org.kie.kogito.StaticApplication; @@ -45,22 +46,29 @@ import org.kie.kogito.process.impl.DefaultProcessEventListenerConfig; import org.kie.kogito.process.impl.DefaultWorkItemHandlerConfig; import org.kie.kogito.process.impl.StaticProcessConfig; -import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; -import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; +import org.kie.kogito.services.jobs.impl.InMemoryProcessJobExecutorFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.Collections.emptyList; +import static org.kie.kogito.services.jobs.impl.StaticJobService.staticJobService; +import static org.kie.kogito.services.uow.StaticUnitOfWorkManger.staticUnitOfWorkManager; public class ProcessTestHelper { private static Logger LOGGER = LoggerFactory.getLogger(ProcessTestHelper.class); public static Application newApplication() { + return newApplication(new StaticProcessConfig()); + } + + public static Application newApplication(ProcessConfig staticConfig) { BpmnProcesses bpmnProcesses = new BpmnProcesses(); - StaticProcessConfig staticConfig = - new StaticProcessConfig(new DefaultWorkItemHandlerConfig(), new DefaultProcessEventListenerConfig(), new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory())); - return new StaticApplication(new StaticConfig(null, staticConfig), bpmnProcesses); + InMemoryJobContext context = new InMemoryJobContext(null, staticUnitOfWorkManager(), bpmnProcesses, null); + staticJobService().clearJobExecutorFactories(); + staticJobService().registerJobExecutorFactory(new InMemoryProcessJobExecutorFactory(context)); + return new StaticApplication(new StaticConfig(Addons.EMTPY, staticConfig), bpmnProcesses); } public static void registerProcessEventListener(Application app, KogitoProcessEventListener kogitoProcessEventListener) { diff --git a/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java b/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java index cf30b7ed2bf..000e78a2d2c 100644 --- a/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java +++ b/jbpm/jbpm-usertask-workitem/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java @@ -35,6 +35,7 @@ import org.kie.kogito.usertask.UserTasks; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle; +import org.kie.kogito.usertask.impl.model.DeadlineHelper; import static java.util.Collections.emptyMap; import static java.util.Optional.ofNullable; @@ -51,12 +52,18 @@ public class UserTaskKogitoWorkItemHandler extends DefaultKogitoWorkItemHandler private static final String DESCRIPTION = "Description"; private static final String PRIORITY = "Priority"; private static final String TASK_NAME = "TaskName"; + private static final String NODE_NAME = "NodeName"; private static final String ACTOR_ID = "ActorId"; private static final String GROUP_ID = "GroupId"; private static final String BUSINESSADMINISTRATOR_ID = "BusinessAdministratorId"; private static final String BUSINESSADMINISTRATOR_GROUP_ID = "BusinessAdministratorGroupId"; private static final String EXCLUDED_OWNER_ID = "ExcludedOwnerId"; + private static final String NOT_STARTED_NOTIFY = "NotStartedNotify"; + private static final String NOT_STARTED_REASSIGN = "NotStartedReassign"; + private static final String NOT_COMPLETED_NOTIFY = "NotCompletedNotify"; + private static final String NOT_COMPLETED_REASSIGN = "NotCompletedReassign"; + public UserTaskKogitoWorkItemHandler() { super(); } @@ -78,7 +85,7 @@ public Optional activateWorkItemHandler(KogitoWorkItemManage userTask.instances().create(instance); - instance.setTaskName((String) workItem.getParameter(TASK_NAME)); + instance.setTaskName(ofNullable((String) workItem.getParameter(TASK_NAME)).orElse((String) workItem.getParameter(NODE_NAME))); instance.setTaskDescription((String) workItem.getParameter(DESCRIPTION)); instance.setTaskPriority(priority != null ? priority.toString() : null); @@ -100,7 +107,12 @@ public Optional activateWorkItemHandler(KogitoWorkItemManage ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_GROUP_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setAdminGroups); ofNullable(workItem.getParameters().get(EXCLUDED_OWNER_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setExcludedUsers); - instance.transition(DefaultUserTaskLifeCycle.ACTIVATE, emptyMap(), IdentityProviders.of(WORKFLOW_ENGINE_USER)); + ofNullable(workItem.getParameters().get(NOT_STARTED_NOTIFY)).map(String.class::cast).map(DeadlineHelper::parseDeadlines).ifPresent(instance::setNotStartedDeadlines); + ofNullable(workItem.getParameters().get(NOT_STARTED_REASSIGN)).map(String.class::cast).map(DeadlineHelper::parseReassignments).ifPresent(instance::setNotStartedReassignments); + ofNullable(workItem.getParameters().get(NOT_COMPLETED_NOTIFY)).map(String.class::cast).map(DeadlineHelper::parseDeadlines).ifPresent(instance::setNotCompletedDeadlines); + ofNullable(workItem.getParameters().get(NOT_COMPLETED_REASSIGN)).map(String.class::cast).map(DeadlineHelper::parseReassignments).ifPresent(instance::setNotCompletedReassignments); + + instance.initialize(emptyMap(), IdentityProviders.of(WORKFLOW_ENGINE_USER)); if (workItem instanceof InternalKogitoWorkItem ikw) { ikw.setExternalReferenceId(instance.getId()); diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/AbstractUserTask.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/AbstractUserTask.java index 795c0f25e9c..8fb214c0b17 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/AbstractUserTask.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/AbstractUserTask.java @@ -20,13 +20,13 @@ import java.util.Collection; import java.util.HashSet; -import java.util.Map; import java.util.Set; import org.kie.kogito.usertask.UserTask; import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.impl.model.DeadlineHelper; import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Notification; import org.kie.kogito.usertask.model.Reassignment; public abstract class AbstractUserTask implements UserTask { @@ -44,10 +44,10 @@ public abstract class AbstractUserTask implements UserTask { private Set adminUsers; private Set adminGroups; private Set excludedUsers; - private Collection>> startDeadlines; - private Collection>> endDeadlines; + private Collection> startDeadlines; + private Collection> endDeadlines; private Collection> startReassigments; - private Collection> endReassigments; + private Collection> endReassignments; public AbstractUserTask(String id, String name) { this.id = id; @@ -61,7 +61,7 @@ public AbstractUserTask(String id, String name) { this.startDeadlines = new HashSet<>(); this.endDeadlines = new HashSet<>(); this.startReassigments = new HashSet<>(); - this.endReassigments = new HashSet<>(); + this.endReassignments = new HashSet<>(); } @Override @@ -77,6 +77,10 @@ public UserTaskInstance createInstance() { instance.setAdminGroups(getAdminGroups()); instance.setExcludedUsers(getExcludedUsers()); instance.setInstances(this.instances()); + instance.setNotCompletedDeadlines(this.getNotCompletedDeadlines()); + instance.setNotCompletedReassignments(this.getNotCompletedReassignments()); + instance.setNotStartedDeadlines(this.getNotStartedDeadlines()); + instance.setNotStartedReassignments(this.getNotStartedReassignments()); return instance; } @@ -200,7 +204,7 @@ public void setExcludedUsers(Set excludedUsers) { } @Override - public Collection>> getNotStartedDeadlines() { + public Collection> getNotStartedDeadlines() { return startDeadlines; } @@ -209,7 +213,7 @@ public void setNotStartedDeadLines(String deadlines) { } @Override - public Collection>> getNotCompletedDeadlines() { + public Collection> getNotCompletedDeadlines() { return endDeadlines; } @@ -227,12 +231,12 @@ public void setNotStartedReassignments(String reassignments) { } @Override - public Collection> getNotCompletedReassigments() { - return endReassigments; + public Collection> getNotCompletedReassignments() { + return endReassignments; } public void setNotCompletedReassigments(String reassignments) { - this.endReassigments = DeadlineHelper.parseReassignments(reassignments); + this.endReassignments = DeadlineHelper.parseReassignments(reassignments); } protected Set toSet(String value) { diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java index 7282ce72532..88ce68e6b90 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java @@ -24,8 +24,10 @@ import org.kie.kogito.auth.IdentityProvider; import org.kie.kogito.jobs.JobsService; import org.kie.kogito.services.identity.NoOpIdentityProvider; +import org.kie.kogito.services.jobs.impl.StaticJobService; import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; +import org.kie.kogito.services.uow.StaticUnitOfWorkManger; import org.kie.kogito.uow.UnitOfWorkManager; import org.kie.kogito.usertask.UserTaskAssignmentStrategyConfig; import org.kie.kogito.usertask.UserTaskConfig; @@ -33,9 +35,15 @@ import org.kie.kogito.usertask.UserTaskInstances; import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle; import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.kie.kogito.services.jobs.impl.StaticJobService.staticJobService; public class DefaultUserTaskConfig implements UserTaskConfig { + private static final Logger LOG = LoggerFactory.getLogger(DefaultUserTaskConfig.class); + private UserTaskEventListenerConfig userTaskEventListeners; private UnitOfWorkManager unitOfWorkManager; private JobsService jobService; @@ -47,7 +55,7 @@ public class DefaultUserTaskConfig implements UserTaskConfig { public DefaultUserTaskConfig() { this(new DefaultUserTaskEventListenerConfig(), new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory()), - null, + staticJobService(), new NoOpIdentityProvider(), new DefaultUserTaskLifeCycle(), new DefaultUserTaskAssignmentStrategyConfig(), @@ -64,8 +72,8 @@ public DefaultUserTaskConfig( Iterable userTaskInstances) { this.userTaskEventListeners = singleton(userTaskEventListenerConfig, DefaultUserTaskEventListenerConfig::new); - this.unitOfWorkManager = singleton(unitOfWorkManager, () -> new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory())); - this.jobService = singleton(jobService, () -> null); + this.unitOfWorkManager = singleton(unitOfWorkManager, StaticUnitOfWorkManger::staticUnitOfWorkManager); + this.jobService = singleton(jobService, StaticJobService::staticJobService); this.identityProvider = singleton(identityProvider, NoOpIdentityProvider::new); this.userTaskLifeCycle = singleton(userTaskLifeCycle, DefaultUserTaskLifeCycle::new); this.userTaskAssignmentStrategyConfig = singleton(userTaskAssignmentStrategyConfig, DefaultUserTaskAssignmentStrategyConfig::new); @@ -73,12 +81,16 @@ public DefaultUserTaskConfig( } - private T singleton(Iterable value, Supplier defaultValue) { - Iterator iterator = value.iterator(); + private T singleton(Iterable values, Supplier defaultValue) { + Iterator iterator = values.iterator(); + T value = null; if (iterator.hasNext()) { - return iterator.next(); + value = iterator.next(); + } else { + value = defaultValue.get(); } - return defaultValue.get(); + LOG.debug("UserTask config element {}", value); + return value; } public DefaultUserTaskConfig( diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java index 757bf30edec..15e2477fc1e 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java @@ -18,24 +18,53 @@ */ package org.kie.kogito.usertask.impl; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; import org.kie.kogito.auth.IdentityProvider; +import org.kie.kogito.auth.IdentityProviders; import org.kie.kogito.internal.usertask.event.KogitoUserTaskEventSupport; import org.kie.kogito.internal.usertask.event.KogitoUserTaskEventSupport.AssignmentType; +import org.kie.kogito.jobs.ExpirationTime; +import org.kie.kogito.jobs.JobDescription; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.jobs.descriptors.UserTaskInstanceJobDescription; import org.kie.kogito.usertask.UserTask; import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.UserTaskInstances; +import org.kie.kogito.usertask.impl.model.DeadlineHelper; import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; import org.kie.kogito.usertask.lifecycle.UserTaskState; import org.kie.kogito.usertask.lifecycle.UserTaskTransitionToken; import org.kie.kogito.usertask.model.Attachment; import org.kie.kogito.usertask.model.Comment; +import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Notification; +import org.kie.kogito.usertask.model.Reassignment; +import org.kie.kogito.usertask.model.ScheduleInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.annotation.JsonIgnore; +import static java.util.Collections.emptyMap; +import static org.kie.kogito.jobs.descriptors.UserTaskInstanceJobDescription.newUserTaskInstanceJobDescriptionBuilder; +import static org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle.WORKFLOW_ENGINE_USER; + public class DefaultUserTaskInstance implements UserTaskInstance { + private static Logger LOG = LoggerFactory.getLogger(DefaultUserTaskInstance.class); + private String id; private String userTaskId; @@ -67,6 +96,24 @@ public class DefaultUserTaskInstance implements UserTaskInstance { private KogitoUserTaskEventSupport userTaskEventSupport; @JsonIgnore private UserTaskLifeCycle userTaskLifeCycle; + @JsonIgnore + private JobsService jobsService; + + private Collection> notStartedDeadlines; + + private Collection> notCompletedDeadlines; + + private Collection> notStartedReassignments; + + private Collection> notCompletedReassigments; + + private Map notStartedDeadlinesTimers; + + private Map notCompletedDeadlinesTimers; + + private Map notStartedReassignmentsTimers; + + private Map notCompletedReassignmentsTimers; public DefaultUserTaskInstance() { this.inputs = new HashMap<>(); @@ -80,6 +127,10 @@ public DefaultUserTaskInstance() { this.adminUsers = new HashSet<>(); this.adminGroups = new HashSet<>(); this.excludedUsers = new HashSet<>(); + this.notStartedDeadlinesTimers = new HashMap<>(); + this.notCompletedDeadlinesTimers = new HashMap<>(); + this.notStartedReassignmentsTimers = new HashMap<>(); + this.notCompletedReassignmentsTimers = new HashMap<>(); } public DefaultUserTaskInstance(UserTask userTask) { @@ -88,6 +139,38 @@ public DefaultUserTaskInstance(UserTask userTask) { this.userTask = userTask; } + public Map getNotCompletedDeadlinesTimers() { + return notCompletedDeadlinesTimers; + } + + public void setNotCompletedDeadlinesTimers(Map notCompletedDeadlinesTimers) { + this.notCompletedDeadlinesTimers = notCompletedDeadlinesTimers; + } + + public Map getNotCompletedReassignmentsTimers() { + return notCompletedReassignmentsTimers; + } + + public void setNotCompletedReassignmentsTimers(Map notCompletedReassignmentsTimers) { + this.notCompletedReassignmentsTimers = notCompletedReassignmentsTimers; + } + + public Map getNotStartedDeadlinesTimers() { + return notStartedDeadlinesTimers; + } + + public void setNotStartedDeadlinesTimers(Map notStartedDeadlinesTimers) { + this.notStartedDeadlinesTimers = notStartedDeadlinesTimers; + } + + public Map getNotStartedReassignmentsTimers() { + return notStartedReassignmentsTimers; + } + + public void setNotStartedReassignmentsTimers(Map notStartedReassignmentsTimers) { + this.notStartedReassignmentsTimers = notStartedReassignmentsTimers; + } + public void setUserTaskEventSupport(KogitoUserTaskEventSupport userTaskEventSupport) { this.userTaskEventSupport = userTaskEventSupport; } @@ -96,6 +179,10 @@ public void setUserTaskLifeCycle(UserTaskLifeCycle userTaskLifeCycle) { this.userTaskLifeCycle = userTaskLifeCycle; } + public UserTaskLifeCycle getUserTaskLifeCycle() { + return userTaskLifeCycle; + } + public void setInstances(UserTaskInstances instances) { this.instances = instances; } @@ -155,6 +242,11 @@ public void setExternalReferenceId(String externalReferenceId) { this.externalReferenceId = externalReferenceId; } + @Override + public void initialize(Map data, IdentityProvider identity) { + transition(this.userTaskLifeCycle.startTransition(), data, identity); + } + @Override public void transition(String transitionId, Map data, IdentityProvider identity) { Optional next = Optional.of(this.userTaskLifeCycle.newTransitionToken(transitionId, this, data)); @@ -522,6 +614,171 @@ public Comment findCommentById(String commentId) { return this.comments.stream().filter(e -> e.getId().equals(commentId)).findAny().orElse(null); } + @Override + public Collection> getNotStartedDeadlines() { + return notStartedDeadlines; + } + + public void setNotStartedDeadlines(Collection> notStartedDeadlines) { + this.notStartedDeadlines = notStartedDeadlines; + } + + @Override + public Collection> getNotCompletedDeadlines() { + return notCompletedDeadlines; + } + + public void setNotCompletedDeadlines(Collection> notCompletedDeadlines) { + this.notCompletedDeadlines = notCompletedDeadlines; + } + + @Override + public Collection> getNotStartedReassignments() { + return notStartedReassignments; + } + + public void setNotStartedReassignments(Collection> notStartedReassignments) { + this.notStartedReassignments = notStartedReassignments; + } + + @Override + public Collection> getNotCompletedReassignments() { + return notCompletedReassigments; + } + + public void setNotCompletedReassignments(Collection> notCompletedReassigments) { + this.notCompletedReassigments = notCompletedReassigments; + } + + public void setJobsService(JobsService jobsService) { + this.jobsService = jobsService; + } + + private void initTimers(Map timers, Collection> deadlines) { + for (DeadlineInfo deadline : deadlines) { + for (JobDescription jobDescription : toJobDescription(deadline)) { + timers.put(jobDescription.id(), deadline.getNotification()); + this.jobsService.scheduleJob(jobDescription); + } + } + this.updatePersistence(); + } + + private void stopTimers(Map timers) { + timers.keySet().stream().filter(Objects::nonNull).forEach(this.jobsService::cancelJob); + timers.clear(); + this.updatePersistence(); + } + + private List toJobDescription(DeadlineInfo deadline) { + List jobs = new ArrayList<>(); + for (ScheduleInfo info : deadline.getScheduleInfo()) { + ExpirationTime expirationTime = DeadlineHelper.getExpirationTime(info); + jobs.add(newUserTaskInstanceJobDescriptionBuilder() + .generateId() + .expirationTime(expirationTime) + .userTaskInstanceId(this.id) + .build()); + } + return jobs; + } + + @Override + public void startNotStartedDeadlines() { + initTimers(this.notStartedDeadlinesTimers, getNotStartedDeadlines()); + } + + @Override + public void startNotStartedReassignments() { + initTimers(this.notStartedReassignmentsTimers, getNotStartedReassignments()); + } + + @Override + public void startNotCompletedDeadlines() { + initTimers(this.notCompletedDeadlinesTimers, getNotCompletedDeadlines()); + } + + @Override + public void startNotCompletedReassignments() { + initTimers(this.notCompletedReassignmentsTimers, getNotCompletedReassignments()); + } + + @Override + public void stopNotStartedDeadlines() { + stopTimers(this.notStartedDeadlinesTimers); + } + + @Override + public void stopNotStartedReassignments() { + stopTimers(this.notStartedReassignmentsTimers); + } + + @Override + public void stopNotCompletedDeadlines() { + stopTimers(this.notCompletedDeadlinesTimers); + } + + @Override + public void stopNotCompletedReassignments() { + stopTimers(this.notCompletedReassignmentsTimers); + } + + public void trigger(UserTaskInstanceJobDescription jobDescription) { + LOG.trace("trigger timer in user tasks {} and job {}", this, jobDescription); + checkAndSendNotitication(jobDescription, notStartedDeadlinesTimers, this::startNotification); + checkAndSendNotitication(jobDescription, notCompletedDeadlinesTimers, this::endNotification); + checkAndReassign(jobDescription, notStartedReassignmentsTimers); + checkAndReassign(jobDescription, notCompletedReassignmentsTimers); + this.updatePersistence(); + } + + private void checkAndSendNotitication(UserTaskInstanceJobDescription timerInstance, Map timers, Consumer publisher) { + Notification notification = timers.get(timerInstance.id()); + if (notification == null) { + return; + } + + if (timerInstance.expirationTime().repeatLimit() <= 0) { + timers.remove(timerInstance.id()); + } + publisher.accept(notification); + + } + + private void startNotification(Notification notification) { + if (this.userTaskEventSupport != null) { + this.userTaskEventSupport.fireOnUserTaskNotStartedDeadline(this, notification.getData()); + } + } + + private void endNotification(Notification notification) { + if (this.userTaskEventSupport != null) { + this.userTaskEventSupport.fireOnUserTaskNotCompletedDeadline(this, notification.getData()); + } + } + + private void checkAndReassign(UserTaskInstanceJobDescription timerInstance, Map timers) { + Reassignment reassignment = timers.remove(timerInstance.id()); + + if (reassignment == null) { + return; + } + reassign(reassignment); + } + + private void reassign(Reassignment reassignment) { + if (!reassignment.getPotentialUsers().isEmpty()) { + setPotentialUsers(reassignment.getPotentialUsers()); + } + if (!reassignment.getPotentialGroups().isEmpty()) { + setPotentialGroups(reassignment.getPotentialGroups()); + } + + this.userTaskLifeCycle.newReassignmentTransitionToken(this, emptyMap()).ifPresent(token -> { + this.userTaskLifeCycle.transition(this, token, IdentityProviders.of(WORKFLOW_ENGINE_USER)); + }); + } + @Override public String toString() { return "DefaultUserTaskInstance [id=" + id + ", status=" + status + ", actualOwner=" + actualOwner + ", taskName=" + taskName + ", taskDescription=" + taskDescription + ", taskPriority=" diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java index e2918673324..40b616f5c8a 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java @@ -80,6 +80,7 @@ public UserTaskInstance disconnect(UserTaskInstance userTaskInstance) { instance.setUserTaskEventSupport(null); instance.setUserTaskLifeCycle(null); instance.setInstances(null); + instance.setJobsService(null); return instance; } @@ -93,6 +94,7 @@ public UserTaskInstance connect(UserTaskInstance userTaskInstance) { instance.setUserTaskEventSupport(impl); instance.setUserTaskLifeCycle(userTaskConfig.userTaskLifeCycle()); instance.setInstances(userTaskInstances); + instance.setJobsService(userTaskConfig.jobsService()); return instance; } } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java index 256144c4849..113228f185c 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java @@ -139,6 +139,7 @@ public boolean exists(String userTaskInstanceId) { @Override public UserTaskInstance create(UserTaskInstance userTaskInstance) { try { + LOG.trace("create {}", userTaskInstance); byte[] data = mapper.writeValueAsBytes(userTaskInstance); userTaskInstances.put(userTaskInstance.getId(), data); return reconnectUserTaskInstance.apply(userTaskInstance); @@ -151,6 +152,7 @@ public UserTaskInstance create(UserTaskInstance userTaskInstance) { @Override public UserTaskInstance update(UserTaskInstance userTaskInstance) { try { + LOG.trace("update {}", userTaskInstance); byte[] data = mapper.writeValueAsBytes(userTaskInstance); userTaskInstances.put(userTaskInstance.getId(), data); return userTaskInstance; @@ -166,6 +168,7 @@ public UserTaskInstance remove(UserTaskInstance userTaskInstance) { if (!userTaskInstances.containsKey(userTaskInstance.getId())) { return null; } + LOG.trace("remove {}", userTaskInstance); userTaskInstances.remove(userTaskInstance.getId()); return disconnectUserTaskInstance.apply(userTaskInstance); } catch (Exception e) { diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java index 3f1009245ec..a2da034e930 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java @@ -79,15 +79,13 @@ private UserTaskView toUserTaskView(UserTaskInstance instance) { @Override public Optional transition(String taskId, String transitionId, Map data, IdentityProvider identity) { - return UnitOfWorkExecutor.executeInUnitOfWork(application.unitOfWorkManager(), () -> { - Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); - if (userTaskInstance.isEmpty()) { - return Optional.empty(); - } - UserTaskInstance ut = userTaskInstance.get(); - ut.transition(transitionId, data, identity); - return Optional.of(toUserTaskView(ut)); - }); + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + ut.transition(transitionId, data, identity); + return Optional.of(toUserTaskView(ut)); } @Override diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java index 6af8a612fcf..5b871d2a8f6 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java @@ -49,6 +49,7 @@ public class DefaultUserTaskLifeCycle implements UserTaskLifeCycle { public static final String COMPLETE = "complete"; public static final String SKIP = "skip"; public static final String FAIL = "fail"; + public static final String REASSIGN = "reassign"; public static final UserTaskState INACTIVE = UserTaskState.initalized(); public static final UserTaskState ACTIVE = UserTaskState.of("Ready"); @@ -66,6 +67,9 @@ public class DefaultUserTaskLifeCycle implements UserTaskLifeCycle { private final UserTaskTransition T_RESERVED_SKIPPED = new DefaultUserTransition(SKIP, RESERVED, OBSOLETE, this::skip); private final UserTaskTransition T_RESERVED_ERROR = new DefaultUserTransition(FAIL, RESERVED, ERROR, this::fail); + private final UserTaskTransition T_RESERVED_ACTIVE_R = new DefaultUserTransition(REASSIGN, RESERVED, ACTIVE, this::reassign); + private final UserTaskTransition T_ACTIVE_ACTIVE_R = new DefaultUserTransition(REASSIGN, ACTIVE, ACTIVE, this::reassign); + private List transitions; public DefaultUserTaskLifeCycle() { @@ -77,7 +81,9 @@ public DefaultUserTaskLifeCycle() { T_RESERVED_ACTIVE, T_RESERVED_COMPLETED, T_RESERVED_SKIPPED, - T_RESERVED_ERROR); + T_RESERVED_ERROR, + T_RESERVED_ACTIVE_R, + T_ACTIVE_ACTIVE_R); } @Override @@ -95,6 +101,15 @@ public Optional transition(UserTaskInstance userTaskIns return transition.executor().execute(userTaskInstance, userTaskTransitionToken, identityProvider); } + @Override + public Optional newReassignmentTransitionToken(UserTaskInstance userTaskInstance, Map data) { + try { + return Optional.of(newTransitionToken(REASSIGN, userTaskInstance.getStatus(), data)); + } catch (UserTaskTransitionException e) { + return Optional.empty(); + } + } + @Override public UserTaskTransitionToken newCompleteTransitionToken(UserTaskInstance userTaskInstance, Map data) { return newTransitionToken(COMPLETE, userTaskInstance.getStatus(), data); @@ -116,11 +131,35 @@ public UserTaskTransitionToken newTransitionToken(String transitionId, UserTaskS return new DefaultUserTaskTransitionToken(transition.id(), transition.source(), transition.target(), data); } + public Optional reassign(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { + userTaskInstance.stopNotStartedDeadlines(); + userTaskInstance.stopNotStartedReassignments(); + userTaskInstance.stopNotCompletedDeadlines(); + userTaskInstance.stopNotCompletedReassignments(); + + // restart the timers + userTaskInstance.startNotCompletedDeadlines(); + userTaskInstance.startNotCompletedReassignments(); + + String user = assignStrategy(userTaskInstance, identityProvider); + if (user != null) { + return Optional.of(newTransitionToken(CLAIM, ACTIVE, Map.of(PARAMETER_USER, user))); + } + userTaskInstance.startNotStartedDeadlines(); + userTaskInstance.startNotStartedReassignments(); + return Optional.empty(); + } + public Optional activate(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { + userTaskInstance.startNotCompletedDeadlines(); + userTaskInstance.startNotCompletedReassignments(); + String user = assignStrategy(userTaskInstance, identityProvider); if (user != null) { return Optional.of(newTransitionToken(CLAIM, ACTIVE, Map.of(PARAMETER_USER, user))); } + userTaskInstance.startNotStartedDeadlines(); + userTaskInstance.startNotStartedReassignments(); return Optional.empty(); } @@ -132,6 +171,8 @@ public Optional claim(UserTaskInstance userTaskInstance defaultUserTaskInstance.setActualOwner(identityProvider.getName()); } } + userTaskInstance.stopNotStartedDeadlines(); + userTaskInstance.stopNotStartedReassignments(); return Optional.empty(); } @@ -144,6 +185,10 @@ public Optional release(UserTaskInstance userTaskInstan public Optional complete(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { token.data().forEach(userTaskInstance::setOutput); + userTaskInstance.stopNotStartedDeadlines(); + userTaskInstance.stopNotStartedReassignments(); + userTaskInstance.stopNotCompletedDeadlines(); + userTaskInstance.stopNotCompletedReassignments(); return Optional.empty(); } @@ -151,6 +196,10 @@ public Optional skip(UserTaskInstance userTaskInstance, if (token.data().containsKey(PARAMETER_NOTIFY)) { userTaskInstance.getMetadata().put(PARAMETER_NOTIFY, token.data().get(PARAMETER_NOTIFY)); } + userTaskInstance.stopNotStartedDeadlines(); + userTaskInstance.stopNotStartedReassignments(); + userTaskInstance.stopNotCompletedDeadlines(); + userTaskInstance.stopNotCompletedReassignments(); return Optional.empty(); } @@ -158,6 +207,10 @@ public Optional fail(UserTaskInstance userTaskInstance, if (token.data().containsKey(PARAMETER_NOTIFY)) { userTaskInstance.getMetadata().put(PARAMETER_NOTIFY, token.data().get(PARAMETER_NOTIFY)); } + userTaskInstance.stopNotStartedDeadlines(); + userTaskInstance.stopNotStartedReassignments(); + userTaskInstance.stopNotCompletedDeadlines(); + userTaskInstance.stopNotCompletedReassignments(); return Optional.empty(); } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/model/DeadlineHelper.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/model/DeadlineHelper.java index cb541a9c7e9..3e0793830b6 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/model/DeadlineHelper.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/model/DeadlineHelper.java @@ -38,6 +38,7 @@ import org.kie.kogito.jobs.ExactExpirationTime; import org.kie.kogito.jobs.ExpirationTime; import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Notification; import org.kie.kogito.usertask.model.Reassignment; import org.kie.kogito.usertask.model.ScheduleInfo; import org.slf4j.Logger; @@ -89,11 +90,17 @@ public static ExpirationTime getExpirationTime(ScheduleInfo info) { } public static Collection> parseReassignments(Object text) { + if (text instanceof String textString && textString.isBlank()) { + return Collections.emptyList(); + } return parseDeadlines(text, DeadlineHelper::parseReassigment, DeadlineHelper::getReassignmentSchedule); } - public static Collection>> parseDeadlines(Object text) { - return parseDeadlines(text, DeadlineHelper::asMap, DeadlineHelper::getSchedulesInfo); + public static Collection> parseDeadlines(Object text) { + if (text instanceof String textString && textString.isBlank()) { + return Collections.emptyList(); + } + return parseDeadlines(text, DeadlineHelper::asNotification, DeadlineHelper::getSchedulesInfo); } private static Collection> parseDeadlines(Object text, @@ -116,7 +123,7 @@ private static DeadlineInfo parseDeadline(String text, deadline.setScheduleInfo(scheduleFunction.apply(text.substring(indexOf + 3, text.length() - 1).trim())); return deadline; } - throw new IllegalArgumentException("Invalid formar for dead line expression " + text); + throw new IllegalArgumentException("Invalid format for dead line expression " + text); } private static Collection getReassignmentSchedule(String timeStr) { @@ -198,6 +205,10 @@ private static int numRepetitions(String repetitionStr) { return repetitionStr.length() > 1 ? Integer.parseInt(repetitionStr.substring(1)) : -1; } + private static Notification asNotification(String text) { + return new Notification(asMap(text)); + } + private static Map asMap(String text) { Map result = new HashMap<>(); for (String str : text.split("\\|")) { diff --git a/jbpm/jbpm-usertask/src/test/java/org/kie/kogito/usertask/impl/model/DeadlineHelperTest.java b/jbpm/jbpm-usertask/src/test/java/org/kie/kogito/usertask/impl/model/DeadlineHelperTest.java index 0bf241c43bb..2c022d7672d 100644 --- a/jbpm/jbpm-usertask/src/test/java/org/kie/kogito/usertask/impl/model/DeadlineHelperTest.java +++ b/jbpm/jbpm-usertask/src/test/java/org/kie/kogito/usertask/impl/model/DeadlineHelperTest.java @@ -23,11 +23,11 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Collection; -import java.util.Map; import org.junit.jupiter.api.Test; import org.kie.kogito.jobs.ExpirationTime; import org.kie.kogito.usertask.model.DeadlineInfo; +import org.kie.kogito.usertask.model.Notification; import org.kie.kogito.usertask.model.Reassignment; import org.kie.kogito.usertask.model.ScheduleInfo; @@ -38,12 +38,12 @@ public class DeadlineHelperTest { @Test public void testRepetition() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:5secs|body:NotCompleted repeated notification every 5secs]@[R/PT5S]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "5secs"); - assertThat(deadlineInfo.getNotification()).containsEntry("body", "NotCompleted repeated notification every 5secs"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "5secs"); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("body", "NotCompleted repeated notification every 5secs"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -61,11 +61,11 @@ public void testRepetition() { @Test public void testRepetitionWithEndDate() { ZonedDateTime future = ZonedDateTime.now().plus(Duration.ofMinutes(2)).plus(Duration.ofSeconds(2)); - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:5secs]@[R/PT5S/" + future.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) + "]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "5secs"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "5secs"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -82,11 +82,11 @@ public void testRepetitionWithEndDate() { @Test public void testRepetitionWithEndDateCornerCase() { ZonedDateTime future = ZonedDateTime.now().plus(Duration.ofSeconds(5)); - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:5secs]@[R/PT5S/" + future.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) + "]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "5secs"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "5secs"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -103,11 +103,11 @@ public void testRepetitionWithEndDateCornerCase() { @Test public void testRepetitionWithEndDateAndLimit() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:5secs]@[R2/PT5S/2021-03-18T18:55:01+01:00]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "5secs"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "5secs"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -124,11 +124,11 @@ public void testRepetitionWithEndDateAndLimit() { @Test public void testRepetitionStartEndDate() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:5secs]@[R1/2021-03-18T18:55:01+01:00/2021-03-18T18:55:06+01:00]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "5secs"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "5secs"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -145,11 +145,11 @@ public void testRepetitionStartEndDate() { @Test public void testRepetitionWithStartDate() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:5secs]@[R26/2021-03-18T18:55:01+01:00/PT2M]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "5secs"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "5secs"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -166,11 +166,11 @@ public void testRepetitionWithStartDate() { @Test public void testExactDate() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:exact date]@[2021-03-18T18:55:01+01:00]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "exact date"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "exact date"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -187,11 +187,11 @@ public void testExactDate() { @Test public void testLargeRepetition() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:more than 1 year]@[R/P1Y3WT1H]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "more than 1 year"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "more than 1 year"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(1); ScheduleInfo scheduleInfo = scheduling.iterator().next(); @@ -209,11 +209,11 @@ public void testLargeRepetition() { @Test public void testMultipleDuration() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:1 and 4 hour]@[PT1H,PT4H]"); assertThat(deadlines).hasSize(1); - DeadlineInfo> deadlineInfo = deadlines.iterator().next(); - assertThat(deadlineInfo.getNotification()).containsEntry("subject", "1 and 4 hour"); + DeadlineInfo deadlineInfo = deadlines.iterator().next(); + assertThat(deadlineInfo.getNotification().getData()).containsEntry("subject", "1 and 4 hour"); Collection scheduling = deadlineInfo.getScheduleInfo(); assertThat(scheduling).hasSize(2); assertThat(scheduling.stream().filter(s -> s.getDuration().equals(Duration.ofHours(1)) || s.getDuration() @@ -222,14 +222,14 @@ public void testMultipleDuration() { @Test public void testMultipleNotification() { - Collection>> deadlines = DeadlineHelper.parseDeadlines( + Collection> deadlines = DeadlineHelper.parseDeadlines( "[subject:1 hour]@[PT1H]^[subject:4 hour]@[PT4H]"); assertThat(deadlines).hasSize(2); - for (DeadlineInfo> deadline : deadlines) { - if ("1 hour".equals(deadline.getNotification().get("subject"))) { + for (DeadlineInfo deadline : deadlines) { + if ("1 hour".equals(deadline.getNotification().getData().get("subject"))) { assertThat(deadline.getScheduleInfo().iterator().next().getDuration()).isEqualTo(Duration.ofHours(1)); - } else if ("4 hour".equals(deadline.getNotification().get("subject"))) { + } else if ("4 hour".equals(deadline.getNotification().getData().get("subject"))) { assertThat(deadline.getScheduleInfo().iterator().next().getDuration()).isEqualTo(Duration.ofHours(4)); } else { fail("Unexpected subject value"); diff --git a/kogito-codegen-modules/kogito-codegen-core/src/main/java/org/kie/kogito/codegen/core/ApplicationConfigGenerator.java b/kogito-codegen-modules/kogito-codegen-core/src/main/java/org/kie/kogito/codegen/core/ApplicationConfigGenerator.java index 7e543431786..5cec2c94a15 100644 --- a/kogito-codegen-modules/kogito-codegen-core/src/main/java/org/kie/kogito/codegen/core/ApplicationConfigGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-core/src/main/java/org/kie/kogito/codegen/core/ApplicationConfigGenerator.java @@ -34,11 +34,12 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.expr.StringLiteralExpr; -import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; +import com.github.javaparser.ast.stmt.BlockStmt; import static org.kie.kogito.codegen.core.CodegenUtils.newObject; @@ -102,7 +103,7 @@ private GeneratedFile generateApplicationConfigDescriptor(Collection con templatedGenerator, "Compilation unit doesn't contain a class or interface declaration!")); - initConfigs(getSuperStatement(cls), configClassNames); + initConfigs(getInitStatement(cls), configClassNames); } return new GeneratedFile(ConfigGenerator.APPLICATION_CONFIG_TYPE, @@ -138,8 +139,8 @@ public void withAddons(Set addons) { this.addons = addons; } - private ExplicitConstructorInvocationStmt getSuperStatement(ClassOrInterfaceDeclaration cls) { - return cls.findFirst(ExplicitConstructorInvocationStmt.class) + private BlockStmt getInitStatement(ClassOrInterfaceDeclaration cls) { + return cls.findFirst(ConstructorDeclaration.class).map(ConstructorDeclaration::getBody) .orElseThrow(() -> new InvalidTemplateException( templatedGenerator, "Impossible to find super invocation")); @@ -154,10 +155,13 @@ private ExplicitConstructorInvocationStmt getSuperStatement(ClassOrInterfaceDecl * @param superInvocation * @param configClassNames */ - private void initConfigs(ExplicitConstructorInvocationStmt superInvocation, Collection configClassNames) { - configClassNames.stream() - .map(config -> new ObjectCreationExpr() - .setType(config)) - .forEach(superInvocation::addArgument); + private void initConfigs(BlockStmt initInvocation, Collection configClassNames) { + initInvocation.findFirst(MethodCallExpr.class).ifPresent(call -> { + configClassNames + .stream() + .map(config -> new ObjectCreationExpr().setType(config)) + .forEach(call::addArgument); + }); + } } diff --git a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigJavaTemplate.java b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigJavaTemplate.java index 64cea466d15..1d872f1d2ff 100644 --- a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigJavaTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigJavaTemplate.java @@ -21,6 +21,6 @@ public class ApplicationConfig extends org.kie.kogito.StaticConfig { public ApplicationConfig() { - super($Addons$ /* additional values provided during codegen */); + init($Addons$ /* additional values provided during codegen */); } } diff --git a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigQuarkusTemplate.java index a329586e4bf..b09fc59ae9b 100644 --- a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigQuarkusTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigQuarkusTemplate.java @@ -24,8 +24,7 @@ public class ApplicationConfig extends org.kie.kogito.StaticConfig { @jakarta.inject.Inject - public ApplicationConfig( - Instance configs) { - super($Addons$, configs); + public ApplicationConfig(Instance configs) { + init($Addons$, configs); } } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigSpringTemplate.java index 74e3c066834..3b403429d60 100644 --- a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigSpringTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/ApplicationConfigSpringTemplate.java @@ -24,8 +24,7 @@ public class ApplicationConfig extends org.kie.kogito.StaticConfig { @org.springframework.beans.factory.annotation.Autowired - public ApplicationConfig( - Collection configs) { - super($Addons$, configs); + public ApplicationConfig(Collection configs) { + init($Addons$, configs); } } diff --git a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperQuarkusTemplate.java index c670e699f90..035438a8143 100644 --- a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperQuarkusTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperQuarkusTemplate.java @@ -42,6 +42,7 @@ public void customize(ObjectMapper mapper) { if (!configBean.failOnEmptyBean()) { mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); } + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true).withTimeZone(TimeZone.getDefault())); mapper.registerModule(JsonFormat.getCloudEventJacksonModule()).findAndRegisterModules(); } diff --git a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperSpringTemplate.java index 86896e5799e..77d853abd9d 100644 --- a/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperSpringTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-core/src/main/resources/class-templates/config/GlobalObjectMapperSpringTemplate.java @@ -47,6 +47,7 @@ public void customize(Jackson2ObjectMapperBuilder builder) { if (!configBean.failOnEmptyBean()) { builder.featuresToDisable (SerializationFeature.FAIL_ON_EMPTY_BEANS); } + builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); builder.dateFormat(new StdDateFormat().withColonInTimeZone(true).withTimeZone(TimeZone.getDefault())); builder.modulesToInstall(new JavaTimeModule()); } diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java index 150c3d62bab..4a6182d0e11 100644 --- a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java @@ -20,11 +20,14 @@ import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import org.kie.kogito.Application; @@ -48,9 +51,13 @@ import org.kie.kogito.process.VariableViolationException; import org.kie.kogito.process.WorkItem; import org.kie.kogito.usertask.UserTaskConfig; +import org.kie.kogito.usertask.UserTaskEventListener; import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.UserTaskInstanceNotAuthorizedException; import org.kie.kogito.usertask.UserTasks; +import org.kie.kogito.usertask.events.UserTaskAssignmentEvent; +import org.kie.kogito.usertask.events.UserTaskDeadlineEvent; +import org.kie.kogito.usertask.events.UserTaskDeadlineEvent.DeadlineType; import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle; import static java.util.Collections.emptyList; @@ -59,6 +66,7 @@ import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle.CLAIM; import static org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle.COMPLETE; import static org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle.RELEASE; @@ -953,4 +961,357 @@ public void testBasicUserTaskProcessWithDuplicatedBusinessKey() throws Exception assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); } + + @Test + public void testUserTaskNotStartedDeadlineWithExpressionReplacement() throws Exception { + CountDownLatch latch = new CountDownLatch(2); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotStartedDeadline.bpmn2"); + assertThat(app).isNotNull(); + List subjects = new ArrayList<>(); + List bodies = new ArrayList<>(); + List types = new ArrayList<>(); + + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskDeadline(UserTaskDeadlineEvent event) { + subjects.add((String) event.getNotification().get("subject")); + bodies.add((String) event.getNotification().get("body")); + types.add(event.getType()); + latch.countDown(); + } + + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("subject", "this is my subject"); + parameters.put("body", "this is my body"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + latch.await(5L, TimeUnit.SECONDS); + String subject = "Task is ready for ${owners[0].id}"; + String body = "\n" + + " \n" + + " Reason this is my subject
\n" + + " body of notification this is my body\n" + + " \n" + + " "; + assertThat(subjects).containsExactly(subject, subject); + assertThat(bodies).containsExactly(body, body); + assertThat(types).containsExactly(DeadlineType.Started, DeadlineType.Started); + } + + @Test + public void testUserTaskNotStartedDeadlineWithExpressionReplacementStopTimer() throws Exception { + CountDownLatch latch = new CountDownLatch(2); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotStartedDeadline.bpmn2"); + assertThat(app).isNotNull(); + + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskDeadline(UserTaskDeadlineEvent event) { + latch.countDown(); + } + + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("subject", "this is my subject"); + parameters.put("body", "this is my body"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + IdentityProvider mary = IdentityProviders.of("mary"); + List userTaskInstances = app.get(UserTasks.class).instances().findByIdentity(mary); + userTaskInstances.forEach(e -> e.transition(DefaultUserTaskLifeCycle.CLAIM, Collections.emptyMap(), mary)); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + assertThat(latch.await(5L, TimeUnit.SECONDS)).isFalse(); + + } + + @Test + public void testUserTaskNotStartedReassign() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotStartedReasssign.bpmn2"); + assertThat(app).isNotNull(); + + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskAssignment(UserTaskAssignmentEvent event) { + List users = Arrays.asList(event.getNewUsersId()); + if (users.size() == 1 && users.contains("mike")) { + latch.countDown(); + } + } + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("subject", "this is my subject"); + parameters.put("body", "this is my body"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + assertThat(latch.await(5L, TimeUnit.SECONDS)).isTrue(); + } + + @Test + public void testUserTaskNotStartedReassignStopTimers() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotStartedReasssign.bpmn2"); + assertThat(app).isNotNull(); + + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskAssignment(UserTaskAssignmentEvent event) { + List users = Arrays.asList(event.getNewUsersId()); + if (users.size() == 1 && users.contains("mike")) { + latch.countDown(); + } + } + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("subject", "this is my subject"); + parameters.put("body", "this is my body"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + IdentityProvider mary = IdentityProviders.of("mary"); + List userTaskInstances = app.get(UserTasks.class).instances().findByIdentity(mary); + userTaskInstances.forEach(e -> e.transition(DefaultUserTaskLifeCycle.CLAIM, Collections.emptyMap(), mary)); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + assertThat(latch.await(2L, TimeUnit.SECONDS)).isFalse(); + } + + @Test + public void testUserTaskNotCompletedDeadlineWithReplacement() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotCompletedDeadline.bpmn2"); + assertThat(app).isNotNull(); + + List subjects = new ArrayList<>(); + List bodies = new ArrayList<>(); + List types = new ArrayList<>(); + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskDeadline(UserTaskDeadlineEvent event) { + subjects.add((String) event.getNotification().get("subject")); + bodies.add((String) event.getNotification().get("body")); + types.add(event.getType()); + latch.countDown(); + } + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("body", "my body!!!"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + assertTrue(latch.await(5L, TimeUnit.SECONDS)); + latch.await(5L, TimeUnit.SECONDS); + String subject = "Not completedTask is ready for ${owners[0].id}"; + String body = "my body!!!"; + assertThat(subjects).containsExactly(subject); + assertThat(bodies).containsExactly(body); + assertThat(types).containsExactly(DeadlineType.Completed); + } + + @Test + public void testUserTaskNotCompletedDeadlineWithReplacementStopTimer() throws Exception { + CountDownLatch latch = new CountDownLatch(2); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotCompletedDeadline.bpmn2"); + assertThat(app).isNotNull(); + + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskDeadline(UserTaskDeadlineEvent event) { + latch.countDown(); + } + + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("subject", "this is my subject"); + parameters.put("body", "this is my body"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + IdentityProvider mary = IdentityProviders.of("mary"); + List userTaskInstances = app.get(UserTasks.class).instances().findByIdentity(mary); + userTaskInstances.forEach(e -> { + e.transition(DefaultUserTaskLifeCycle.CLAIM, emptyMap(), mary); + e.transition(DefaultUserTaskLifeCycle.COMPLETE, emptyMap(), mary); + }); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + assertThat(latch.await(5L, TimeUnit.SECONDS)).isFalse(); + + } + + @Test + public void testUserTaskNotCompletedReassign() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotCompletedReassign.bpmn2"); + assertThat(app).isNotNull(); + + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskAssignment(UserTaskAssignmentEvent event) { + List users = Arrays.asList(event.getNewUsersId()); + if (users.size() == 1 && users.contains("mike")) { + latch.countDown(); + } + } + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("body", "my body!!!"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + assertTrue(latch.await(5L, TimeUnit.SECONDS)); + + } + + @Test + public void testUserTaskNotCompletedReassignStopTimer() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + Application app = generateCodeProcessesOnly("usertask/UserTasksNotCompletedReassign.bpmn2"); + assertThat(app).isNotNull(); + + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskEventListener() { + @Override + public void onUserTaskAssignment(UserTaskAssignmentEvent event) { + List users = Arrays.asList(event.getNewUsersId()); + if (users.size() == 1 && users.contains("mike")) { + latch.countDown(); + } + } + + }); + Process p = app.get(Processes.class).processById("UserTasksDeadline"); + + Model m = p.createModel(); + Map parameters = new HashMap<>(); + parameters.put("subject", "this is my subject"); + parameters.put("body", "this is my body"); + m.fromMap(parameters); + + // assign custom business key for process instance + String businessKey = "business key"; + ProcessInstance processInstance = p.createInstance(businessKey, m); + processInstance.start(); + + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); + // verify that custom business key is assigned properly + assertThat(processInstance.businessKey()).isEqualTo(businessKey); + IdentityProvider mary = IdentityProviders.of("mary"); + List userTaskInstances = app.get(UserTasks.class).instances().findByIdentity(mary); + userTaskInstances.forEach(e -> { + e.transition(DefaultUserTaskLifeCycle.CLAIM, emptyMap(), mary); + e.transition(DefaultUserTaskLifeCycle.COMPLETE, emptyMap(), mary); + }); + + // start another process instance with assigned duplicated business key of already active instance + ProcessInstance otherProcessInstance = p.createInstance(businessKey, m); + assertThat(otherProcessInstance.id()).isNotEqualTo(processInstance.id()); + assertThat(otherProcessInstance.businessKey()).isEqualTo(processInstance.businessKey()).isEqualTo(businessKey); + assertThat(latch.await(2L, TimeUnit.SECONDS)).isFalse(); + + } } diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotCompletedDeadline.bpmn2 b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotCompletedDeadline.bpmn2 new file mode 100644 index 00000000000..1c7eb2d169e --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotCompletedDeadline.bpmn2 @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + _2_NotCompletedNotifyInput + _2_NotStartedNotifyInput + _2_CommentInput + _2_NotCompletedReassignInput + _2_SkippableInput + _2_NotStartedReassignInput + _2_TaskNameInput + _2_GroupIdInput + _2_PriorityInput + + + + + + _2_NotCompletedNotifyInput + + [tousers:mike|subject:Not completedTask is ready for ${owners[0].id}|body:#{body}]@[PT2S] + _2_NotCompletedNotifyInput + + + + _2_SkippableInput + + false + _2_SkippableInput + + + + _2_TaskNameInput + + + _2_TaskNameInput + + + + _2_GroupIdInput + + + _2_GroupIdInput + + + + _2_PriorityInput + + + _2_PriorityInput + + + + + john,mary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotCompletedReassign.bpmn2 b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotCompletedReassign.bpmn2 new file mode 100644 index 00000000000..c0f0b33eb80 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotCompletedReassign.bpmn2 @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + _2_NotCompletedNotifyInput + _2_NotStartedNotifyInput + _2_CommentInput + _2_NotCompletedReassignInput + _2_SkippableInput + _2_NotStartedReassignInput + _2_TaskNameInput + _2_GroupIdInput + _2_PriorityInput + + + + + + _2_NotCompletedReassignInput + + [users:mike]@[2s] + _2_NotCompletedReassignInput + + + + _2_SkippableInput + + false + _2_SkippableInput + + + + _2_TaskNameInput + + + _2_TaskNameInput + + + + _2_GroupIdInput + + + _2_GroupIdInput + + + + _2_PriorityInput + + + _2_PriorityInput + + + + + john,mary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotStartedDeadline.bpmn2 b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotStartedDeadline.bpmn2 new file mode 100644 index 00000000000..d05098a7bf5 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotStartedDeadline.bpmn2 @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + _2_NotCompletedNotifyInput + _2_NotStartedNotifyInput + _2_CommentInput + _2_NotCompletedReassignInput + _2_SkippableInput + _2_NotStartedReassignInput + _2_TaskNameInput + _2_GroupIdInput + _2_PriorityInput + + + + + + _2_NotStartedNotifyInput + + [tousers:mike|subject:Task is ready for ${owners[0].id}|body:<html> + <body> + Reason #{subject}<br/> + body of notification #{body} + </body> + </html>]@[PT2S,PT4S] + _2_NotStartedNotifyInput + + + + _2_SkippableInput + + false + _2_SkippableInput + + + + _2_TaskNameInput + + + _2_TaskNameInput + + + + _2_GroupIdInput + + + _2_GroupIdInput + + + + _2_PriorityInput + + + _2_PriorityInput + + + + + john,mary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotStartedReasssign.bpmn2 b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotStartedReasssign.bpmn2 new file mode 100644 index 00000000000..c2f69199e33 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/usertask/UserTasksNotStartedReasssign.bpmn2 @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + _2_NotCompletedNotifyInput + _2_NotStartedNotifyInput + _2_CommentInput + _2_NotCompletedReassignInput + _2_SkippableInput + _2_NotStartedReassignInput + _2_TaskNameInput + _2_GroupIdInput + _2_PriorityInput + + + + + + _2_NotStartedReassignInput + + [users:mike]@[PT1S] + _2_NotStartedReassignInput + + + + _2_SkippableInput + + false + _2_SkippableInput + + + + _2_TaskNameInput + + + _2_TaskNameInput + + + + _2_GroupIdInput + + + _2_GroupIdInput + + + + _2_PriorityInput + + + _2_PriorityInput + + + + + john,mary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java index 7fd6603aa04..9f4e1abb8d0 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java @@ -18,7 +18,6 @@ */ package org.kie.kogito.codegen.usertask; -import java.io.File; import java.io.IOException; import java.io.Reader; import java.nio.file.Path; @@ -56,6 +55,9 @@ import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess; import org.kie.kogito.process.validation.ValidationException; import org.kie.kogito.process.validation.ValidationLogDecorator; +import org.kie.kogito.usertask.impl.model.DeadlineHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import com.github.javaparser.StaticJavaParser; @@ -79,6 +81,8 @@ public class UserTaskCodegen extends AbstractGenerator { + private static Logger LOG = LoggerFactory.getLogger(UserTaskCodegen.class); + private static final String NODE_NAME = "NodeName"; private static final String DESCRIPTION = "Description"; private static final String PRIORITY = "Priority"; @@ -167,8 +171,8 @@ public GeneratedFile generateRestEndpiont() { compilationUnit.findAll(MethodDeclaration.class).stream().filter(MethodDeclaration::isPublic).forEach(context().getDependencyInjectionAnnotator()::withTransactional); } String className = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class).get().getNameAsString(); - String urlBase = packageName.replaceAll("\\.", File.separator); - return new GeneratedFile(GeneratedFileType.REST, Path.of(urlBase).resolve(className + ".java"), compilationUnit.toString()); + Path basePath = UserTaskCodegenHelper.path(packageName); + return new GeneratedFile(GeneratedFileType.REST, basePath.resolve(className + ".java"), compilationUnit.toString()); } public GeneratedFile generateProducer() { @@ -176,8 +180,8 @@ public GeneratedFile generateProducer() { CompilationUnit compilationUnit = producerTemplateGenerator.compilationUnitOrThrow("No producer template found for user tasks"); compilationUnit.setPackageDeclaration(packageName); String className = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class).get().getNameAsString(); - String urlBase = packageName.replaceAll("\\.", File.separator); - return new GeneratedFile(GeneratedFileType.SOURCE, Path.of(urlBase).resolve(className + ".java"), compilationUnit.toString()); + Path basePath = UserTaskCodegenHelper.path(packageName); + return new GeneratedFile(GeneratedFileType.SOURCE, basePath.resolve(className + ".java"), compilationUnit.toString()); } public List generateUserTask() { @@ -220,16 +224,28 @@ public List generateUserTask() { block.addStatement(new MethodCallExpr(new ThisExpr(), "setReferenceName", NodeList.nodeList(toStringExpression(info.getParameter(NODE_NAME))))); block.addStatement(new MethodCallExpr(new ThisExpr(), "setSkippable", NodeList.nodeList(toStringExpression(info.getParameter("Skippable"))))); - block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotStartedDeadLines", NodeList.nodeList(toStringExpression(info.getParameter("NotStartedNotify"))))); - block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotCompletedDeadlines", NodeList.nodeList(toStringExpression(info.getParameter("NotCompletedNotify"))))); - block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotStartedReassignments", NodeList.nodeList(toStringExpression(info.getParameter("NotCompletedReassign"))))); - block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotCompletedReassigments", NodeList.nodeList(toStringExpression(info.getParameter("NotStartedReassign"))))); + block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotStartedDeadLines", NodeList.nodeList(toDeadlineExpression(info.getParameter("NotStartedNotify"))))); + block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotCompletedDeadlines", NodeList.nodeList(toDeadlineExpression(info.getParameter("NotCompletedNotify"))))); + block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotStartedReassignments", NodeList.nodeList(toDeadlineExpression(info.getParameter("NotStartedReassign"))))); + block.addStatement(new MethodCallExpr(new ThisExpr(), "setNotCompletedReassigments", NodeList.nodeList(toDeadlineExpression(info.getParameter("NotCompletedReassign"))))); generatedFiles.add(new GeneratedFile(GeneratedFileType.SOURCE, UserTaskCodegenHelper.path(info).resolve(className + ".java"), unit.toString())); } return generatedFiles; } + private Expression toDeadlineExpression(Object parameter) { + if (parameter instanceof String stringParam) { + try { + DeadlineHelper.parseDeadlines(stringParam); + return toStringExpression(stringParam); + } catch (Exception e) { + LOG.debug("to deadline calculation failure. {} it is not a proper expression"); + } + } + return new CastExpr(StaticJavaParser.parseType(String.class.getName()), new NullLiteralExpr()); + } + private Expression toStringExpression(Object value) { if (value == null) { return new CastExpr(StaticJavaParser.parseType(String.class.getName()), new NullLiteralExpr()); diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegenHelper.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegenHelper.java index d61e4b9ab8b..996221f26c8 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegenHelper.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegenHelper.java @@ -19,8 +19,8 @@ package org.kie.kogito.codegen.usertask; -import java.io.File; import java.nio.file.Path; +import java.util.Arrays; import org.jbpm.process.core.Work; import org.kie.kogito.internal.utils.ConversionUtils; @@ -44,7 +44,17 @@ public static String packageName(Work descriptor) { } public static Path path(Work descriptor) { - return Path.of(((String) descriptor.getParameter("PackageName")).replaceAll("\\.", File.separator)); + return path(packageName(descriptor)); + } + + public static Path path(String packageName) { + String[] pathFragments = packageName.split("\\."); + + if (pathFragments.length == 1) { + return Path.of(pathFragments[0]); + } + String[] children = Arrays.copyOfRange(pathFragments, 1, pathFragments.length); + return Path.of(pathFragments[0], children); } public static String fqnClassName(Work descriptor) { diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java index d6483f92b28..f262ff6c580 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java @@ -18,7 +18,6 @@ */ package org.kie.kogito.codegen.usertask; -import java.io.File; import java.nio.file.Path; import java.util.List; @@ -58,8 +57,9 @@ public GeneratedFile generate() { ConstructorDeclaration declaration = clazzDeclaration.findFirst(ConstructorDeclaration.class).get(); declaration.setName(configClassName()); + Path basePath = UserTaskCodegenHelper.path(packageName); - return new GeneratedFile(GeneratedFileType.SOURCE, Path.of(packageName.replaceAll("\\.", File.separator), configClassName() + ".java"), unit.toString()); + return new GeneratedFile(GeneratedFileType.SOURCE, basePath.resolve(configClassName() + ".java"), unit.toString()); } } diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskContainerGenerator.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskContainerGenerator.java index 364ecf03831..1b05ea184fa 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskContainerGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskContainerGenerator.java @@ -62,17 +62,15 @@ public CompilationUnit compilationUnit() { ClassOrInterfaceDeclaration clazzUnit = unit.findFirst(ClassOrInterfaceDeclaration.class).get(); ConstructorDeclaration constructor = clazzUnit.findFirst(ConstructorDeclaration.class).get(); - - BlockStmt block = new BlockStmt(); + BlockStmt block = constructor.getBody(); NodeList arguments = new NodeList<>(); arguments.add(new NameExpr("application")); for (Work descriptor : descriptors) { String fqn = UserTaskCodegenHelper.fqnClassName(descriptor); arguments.add(new ObjectCreationExpr().setType(StaticJavaParser.parseClassOrInterfaceType(fqn)).setArguments(nodeList(new NameExpr("application")))); } - block.addStatement(new ExplicitConstructorInvocationStmt().setThis(false).setArguments(arguments)); + block.findFirst(ExplicitConstructorInvocationStmt.class).ifPresent(e -> e.setArguments(arguments)); constructor.setBody(block); - return unit; } diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/ProcessContainerJavaTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/ProcessContainerJavaTemplate.java index 341baa98a8d..40c9fa34862 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/ProcessContainerJavaTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/ProcessContainerJavaTemplate.java @@ -18,6 +18,14 @@ */ package $Package$; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.process.ProcessConfig; +import org.kie.kogito.process.impl.StaticProcessConfig; +import org.kie.kogito.services.jobs.impl.InMemoryProcessJobExecutorFactory; +import org.kie.kogito.services.jobs.impl.InMemoryJobService; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; +import org.kie.kogito.uow.UnitOfWorkManager; + public class Processes implements org.kie.kogito.process.Processes { private final Application application; @@ -25,6 +33,13 @@ public class Processes implements org.kie.kogito.process.Processes { public Processes(Application application) { this.application = application; + JobsService jobsService = this.application.config().get(ProcessConfig.class).jobsService(); + UnitOfWorkManager unitOfWorkManager = this.application.config().get(ProcessConfig.class).unitOfWorkManager(); + if (jobsService instanceof InMemoryJobService) { + InMemoryJobService inMemoryJobService = (InMemoryJobService) jobsService; + InMemoryJobContext context = new InMemoryJobContext(null, unitOfWorkManager, this, null); + inMemoryJobService.registerJobExecutorFactory(new InMemoryProcessJobExecutorFactory(context)); + } } public org.kie.kogito.process.Process processById(String processId) { diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerJavaTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerJavaTemplate.java index 3b5293ac0e3..846b9a88b2b 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerJavaTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerJavaTemplate.java @@ -18,12 +18,25 @@ */ package org.kie.kogito.usertask.impl; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.usertask.UserTaskConfig; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; +import org.kie.kogito.services.jobs.impl.InMemoryJobService; +import org.kie.kogito.services.jobs.impl.InMemoryUserTaskJobExecutorFactory; +import org.kie.kogito.uow.UnitOfWorkManager; import org.kie.kogito.usertask.impl.DefaultUserTasks; public class UserTasks extends DefaultUserTasks { public UserTasks(Application application) { super(application); + JobsService jobsService = application.config().get(UserTaskConfig.class).jobsService(); + UnitOfWorkManager unitOfWorkManager = application.config().get(UserTaskConfig.class).unitOfWorkManager(); + if (jobsService instanceof InMemoryJobService) { + InMemoryJobService inMemoryJobService = (InMemoryJobService) jobsService; + InMemoryJobContext context = new InMemoryJobContext(null, unitOfWorkManager, null, this); + inMemoryJobService.registerJobExecutorFactory(new InMemoryUserTaskJobExecutorFactory(context)); + } } } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java index 8e98179784d..7b341ab3bbe 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java @@ -23,6 +23,11 @@ import java.util.Map; import java.util.List; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; +import org.kie.kogito.services.jobs.impl.InMemoryJobService; +import org.kie.kogito.services.jobs.impl.InMemoryUserTaskJobExecutorFactory; +import org.kie.kogito.uow.UnitOfWorkManager; import org.kie.kogito.uow.events.UnitOfWorkUserTaskEventListener; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; import org.kie.kogito.usertask.impl.KogitoUserTaskEventSupportImpl; @@ -57,6 +62,13 @@ public void setup() { mappedUserTask.put(userTask.id(), userTask); LOG.info("Registering user task {} with task name {}", userTask.id(), userTask.getTaskName()); } + JobsService jobsService = application.config().get(UserTaskConfig.class).jobsService(); + UnitOfWorkManager unitOfWorkManager = application.config().get(UserTaskConfig.class).unitOfWorkManager(); + if (jobsService instanceof InMemoryJobService) { + InMemoryJobService inMemoryJobService = (InMemoryJobService) jobsService; + InMemoryJobContext context = new InMemoryJobContext(null, unitOfWorkManager, null, this); + inMemoryJobService.registerJobExecutorFactory(new InMemoryUserTaskJobExecutorFactory(context)); + } } public UserTask userTaskById(String userTaskId) { @@ -78,6 +90,7 @@ private UserTaskInstance disconnect(UserTaskInstance userTaskInstance) { instance.setUserTaskEventSupport(null); instance.setUserTaskLifeCycle(null); instance.setInstances(null); + instance.setJobsService(null); return instance; } @@ -91,6 +104,7 @@ public UserTaskInstance connect(UserTaskInstance userTaskInstance) { instance.setUserTaskEventSupport(impl); instance.setUserTaskLifeCycle(userTaskConfig.userTaskLifeCycle()); instance.setInstances(application.config().get(UserTaskConfig.class).userTaskInstances()); + instance.setJobsService(userTaskConfig.jobsService()); return instance; } } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java index 9785b44d7c4..0d175846868 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java @@ -24,6 +24,11 @@ import java.util.List; import org.kie.kogito.Application; +import org.kie.kogito.jobs.JobsService; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; +import org.kie.kogito.services.jobs.impl.InMemoryJobService; +import org.kie.kogito.services.jobs.impl.InMemoryUserTaskJobExecutorFactory; +import org.kie.kogito.uow.UnitOfWorkManager; import org.kie.kogito.uow.events.UnitOfWorkUserTaskEventListener; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; import org.kie.kogito.usertask.impl.KogitoUserTaskEventSupportImpl; @@ -58,6 +63,13 @@ public void setup() { mappedUserTask.put(userTask.id(), userTask); LOG.info("Registering user task {} with task name {}", userTask.id(), userTask.getTaskName()); } + JobsService jobsService = application.config().get(UserTaskConfig.class).jobsService(); + UnitOfWorkManager unitOfWorkManager = application.config().get(UserTaskConfig.class).unitOfWorkManager(); + if (jobsService instanceof InMemoryJobService) { + InMemoryJobService inMemoryJobService = (InMemoryJobService) jobsService; + InMemoryJobContext context = new InMemoryJobContext(null, unitOfWorkManager, null, this); + inMemoryJobService.registerJobExecutorFactory(new InMemoryUserTaskJobExecutorFactory(context)); + } } public UserTask userTaskById(String userTaskId) { @@ -79,6 +91,7 @@ private UserTaskInstance disconnect(UserTaskInstance userTaskInstance) { instance.setUserTaskEventSupport(null); instance.setUserTaskLifeCycle(null); instance.setInstances(null); + instance.setJobsService(null); return instance; } @@ -92,6 +105,7 @@ public UserTaskInstance connect(UserTaskInstance userTaskInstance) { instance.setUserTaskEventSupport(impl); instance.setUserTaskLifeCycle(userTaskConfig.userTaskLifeCycle()); instance.setInstances(application.config().get(UserTaskConfig.class).userTaskInstances()); + instance.setJobsService(userTaskConfig.jobsService()); return instance; } } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/usertask/UserTaskCodegenHelperTest.java b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/usertask/UserTaskCodegenHelperTest.java new file mode 100644 index 00000000000..1e1beb05710 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/usertask/UserTaskCodegenHelperTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.kie.kogito.codegen.usertask; + +import java.nio.file.Path; + +import org.jbpm.process.core.Work; +import org.jbpm.process.core.impl.WorkImpl; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class UserTaskCodegenHelperTest { + static final String PROCESS_ID = "approvals"; + static final String TASK_ID = "taskId"; + static final String PACKAGE = "org.kie.kogito.usertask"; + static final Path PACKAGE_PATH = Path.of("org/kie/kogito/usertask"); + + private Work work; + + @BeforeEach + void setUp() { + work = new WorkImpl(); + work.setParameter("ProcessId", PROCESS_ID); + work.setParameter(Work.PARAMETER_UNIQUE_TASK_ID, TASK_ID); + work.setParameter("PackageName", PACKAGE); + } + + @Test + void testGetWorkProcessId() { + assertThat(UserTaskCodegenHelper.processId(work)).isEqualTo("Approvals"); + } + + @Test + void testGetWorkClassName() { + assertThat(UserTaskCodegenHelper.className(work)).isEqualTo("Approvals_TaskId"); + } + + @Test + void testGetWorkPackagePath() { + assertThat(UserTaskCodegenHelper.path(work)) + .isNotNull() + .isEqualTo(PACKAGE_PATH); + + } + + @Test + void testGetPath() { + assertThat(UserTaskCodegenHelper.path(PACKAGE)) + .isNotNull() + .isEqualTo(PACKAGE_PATH); + + assertThat(UserTaskCodegenHelper.path("test")) + .isNotNull() + .isEqualTo(Path.of("test")); + } +} diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java index 49e16462cc3..5e4c075e69d 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java @@ -68,7 +68,6 @@ public class ServerlessWorkflowParser { public static final String NODE_START_NAME = "Start"; public static final String NODE_END_NAME = "End"; - public static final String DEFAULT_NAME = "workflow"; public static final String DEFAULT_PACKAGE = "org.kie.kogito.serverless"; public static final String DEFAULT_VERSION = "1.0"; @@ -127,7 +126,7 @@ private ServerlessWorkflowParser(Workflow workflow, KogitoBuildContext context) private GeneratedInfo parseProcess() { RuleFlowProcessFactory factory = RuleFlowProcessFactory.createProcess(workflow.getId(), !workflow.isKeepActive()) - .name(workflow.getName() == null ? DEFAULT_NAME : workflow.getName()) + .name(workflow.getName() == null ? workflow.getId() : workflow.getName()) .version(workflow.getVersion() == null ? DEFAULT_VERSION : workflow.getVersion()) .packageName(workflow.getMetadata() != null ? workflow.getMetadata().getOrDefault("package", DEFAULT_PACKAGE) : DEFAULT_PACKAGE) diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/validation/WorkflowValidator.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/validation/WorkflowValidator.java index 82de55e0af7..21cd5af355c 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/validation/WorkflowValidator.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/validation/WorkflowValidator.java @@ -18,11 +18,14 @@ */ package org.kie.kogito.serverless.workflow.parser.handlers.validation; +import java.util.Map; + import org.kie.kogito.serverless.workflow.parser.ParserContext; import io.serverlessworkflow.api.Workflow; import static org.kie.kogito.internal.utils.ConversionUtils.isEmpty; +import static org.kie.kogito.serverless.workflow.utils.ServerlessWorkflowUtils.findDuplicates; public class WorkflowValidator { @@ -33,5 +36,13 @@ public static void validateStart(Workflow workflow, ParserContext context) { if (workflow.getStart() == null || isEmpty(workflow.getStart().getStateName())) { context.addValidationError("Workflow does not define a starting state"); } + if (workflow.getFunctions() != null) { + Map functionDuplicates = findDuplicates(workflow.getFunctions().getFunctionDefs(), f -> f.getName()); + if (!functionDuplicates.isEmpty()) { + StringBuilder sb = new StringBuilder("There are duplicated function definitions: "); + functionDuplicates.forEach((k, v) -> sb.append(String.format("\nFunction %s appears %d times", k, v))); + context.addValidationError(sb.toString()); + } + } } } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/ServerlessWorkflowUtils.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/ServerlessWorkflowUtils.java index e21330ef2e1..13a04e970b2 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/ServerlessWorkflowUtils.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/ServerlessWorkflowUtils.java @@ -27,8 +27,13 @@ import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -251,4 +256,19 @@ private static ModelMetaData getModelMetadata(WorkflowProcess process, Class return new ModelMetaData(process.getId(), modelClass.getPackage().getName(), modelClass.getSimpleName(), KogitoWorkflowProcess.PUBLIC_VISIBILITY, VariableDeclarations.of(Collections.emptyMap()), false); } + + public static Map findDuplicates(List items, Function converter) { + if (items == null) { + return Map.of(); + } + Map duplicates = new LinkedHashMap<>(); + Set helper = new HashSet<>(); + items.forEach(item -> { + V toAdd = converter.apply(item); + if (!helper.add(toAdd)) { + duplicates.compute(toAdd, (k, v) -> v == null ? 2 : ++v); + } + }); + return duplicates; + } } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java index e140cb54b87..6041dbbdf99 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java @@ -642,7 +642,7 @@ public void testMinimumWorkflow() { ServerlessWorkflowParser parser = ServerlessWorkflowParser.of(workflow, JavaKogitoBuildContext.builder().build()); Process process = parser.getProcessInfo().info(); assertThat(parser.getProcessInfo().info()).isSameAs(process); - assertThat(process.getName()).isEqualTo(ServerlessWorkflowParser.DEFAULT_NAME); + assertThat(process.getName()).isEqualTo(workflow.getId()); assertThat(process.getVersion()).isEqualTo(ServerlessWorkflowParser.DEFAULT_VERSION); assertThat(process.getPackageName()).isEqualTo(ServerlessWorkflowParser.DEFAULT_PACKAGE); } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/WorkflowUtilsTest.java b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/WorkflowUtilsTest.java index 68749171037..a2c8b9dcb43 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/WorkflowUtilsTest.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/WorkflowUtilsTest.java @@ -19,7 +19,9 @@ package org.kie.kogito.serverless.workflow.utils; import java.io.File; +import java.util.Arrays; import java.util.Collections; +import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -51,4 +53,9 @@ public void testResolveFunctionMetadata() { assertThat(resolveFunctionMetadata(function, "testprop1", context)).isNotNull().isEqualTo("customtestprop1val"); assertThat(resolveFunctionMetadata(function, "testprop2", context)).isNotNull().isEqualTo("testprop2val"); } + + @Test + void findDuplicates() { + assertThat(ServerlessWorkflowUtils.findDuplicates(Arrays.asList(1, 3, 3, 2, 2, 1, 8, 7, 3), v -> v)).isEqualTo(Map.of(3, 3, 2, 2, 1, 2)); + } } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticFluentWorkflowApplicationTest.java b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticFluentWorkflowApplicationTest.java index 5cf827f291c..dfc9d750d39 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticFluentWorkflowApplicationTest.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticFluentWorkflowApplicationTest.java @@ -28,7 +28,10 @@ import org.junit.jupiter.api.Test; import org.kie.api.event.process.ProcessCompletedEvent; import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener; +import org.kie.kogito.jobs.JobsService; import org.kie.kogito.process.Process; +import org.kie.kogito.process.ProcessConfig; +import org.kie.kogito.process.Processes; import org.kie.kogito.process.validation.ValidationException; import org.kie.kogito.serverless.workflow.actions.SysoutAction; import org.kie.kogito.serverless.workflow.actions.WorkflowLogLevel; @@ -38,6 +41,10 @@ import org.kie.kogito.serverless.workflow.parser.types.SysOutTypeHandler; import org.kie.kogito.serverless.workflow.utils.ExpressionHandlerUtils; import org.kie.kogito.serverless.workflow.utils.KogitoProcessContextResolver; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; +import org.kie.kogito.services.jobs.impl.InMemoryJobService; +import org.kie.kogito.services.jobs.impl.InMemoryProcessJobExecutorFactory; +import org.kie.kogito.uow.UnitOfWorkManager; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; @@ -141,6 +148,13 @@ void testSwitchLoop() throws InterruptedException, TimeoutException { OperationStateBuilder sleepState = operation().action(call(expr("inc", ".count=.count+1")).sleepAfter(Duration.ofSeconds(1))); try (StaticWorkflowApplication application = StaticWorkflowApplication.create()) { + ProcessConfig processConfig = application.config().get(ProcessConfig.class); + JobsService jobService = processConfig.jobsService(); + Processes processes = application.get(Processes.class); + UnitOfWorkManager unitOfWorkManager = processConfig.unitOfWorkManager(); + if (jobService instanceof InMemoryJobService inMemoryJobService) { + inMemoryJobService.registerJobExecutorFactory(new InMemoryProcessJobExecutorFactory(new InMemoryJobContext(null, unitOfWorkManager, processes, null))); + } Workflow workflow = workflow("Polling").start(startTask) .next(sleepState) .next(pollTask) diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java index bed9c81d532..1c5612782e6 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java @@ -115,8 +115,9 @@ void testValidationError() throws IOException { StaticWorkflowApplication application = StaticWorkflowApplication.create()) { Workflow workflow = getWorkflow(reader, WorkflowFormat.JSON); ValidationException validationException = catchThrowableOfType(() -> application.process(workflow), ValidationException.class); - assertThat(validationException.getErrors()).hasSizeGreaterThanOrEqualTo(4); - assertThat(validationException).hasMessageContaining("error").hasMessageContaining("function").hasMessageContaining("connect").hasMessageContaining("transition"); + assertThat(validationException.getErrors()).hasSizeGreaterThanOrEqualTo(5); + assertThat(validationException).hasMessageContaining("error").hasMessageContaining("function").hasMessageContaining("connect").hasMessageContaining("transition") + .hasMessageContaining("duplicated"); } } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/resources/wrong.sw.json b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/resources/wrong.sw.json index 41c48f309a9..848da0b48e3 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/resources/wrong.sw.json +++ b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/resources/wrong.sw.json @@ -19,6 +19,11 @@ "name": "logInfo", "type": "custom", "operation": "sysout:INFO" + }, + { + "name": "pushData", + "type": "custom", + "operation": "script:python:print('javierito')" } ], "errors":[], diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/StateBuilder.java b/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/StateBuilder.java index ca01f32307a..21460baf12b 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/StateBuilder.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/StateBuilder.java @@ -58,6 +58,7 @@ public static ForEachStateBuilder forEach(String inputExpr) { protected final S state; protected final Collection functionDefinitions = new HashSet<>(); protected final Collection eventDefinitions = new HashSet<>(); + private short buildCount; Collection getFunctions() { return functionDefinitions; @@ -115,9 +116,14 @@ public T outputFilter(String filter) { } public S build() { + buildCount++; return ensureName(state); } + short buildCount() { + return buildCount; + } + private static int counter; protected static T ensureName(T state) { diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/TransitionBuilder.java b/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/TransitionBuilder.java index d6736245a19..10ad1020f2f 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/TransitionBuilder.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/TransitionBuilder.java @@ -43,8 +43,10 @@ protected TransitionBuilder(T container, WorkflowBuilder workflow, DefaultState public TransitionBuilder next(StateBuilder stateBuilder) { DefaultState state = stateBuilder.build(); - workflow.addFunctions(stateBuilder.getFunctions()); - workflow.addEvents(stateBuilder.getEvents()); + if (stateBuilder.buildCount() == 1) { + workflow.addFunctions(stateBuilder.getFunctions()); + workflow.addEvents(stateBuilder.getEvents()); + } next(state); lastState = state; return this; diff --git a/kogito-workitems/kogito-jackson-utils/src/main/java/org/kie/kogito/jackson/utils/CommonObjectModule.java b/kogito-workitems/kogito-jackson-utils/src/main/java/org/kie/kogito/jackson/utils/CommonObjectModule.java new file mode 100644 index 00000000000..09cbe9371e5 --- /dev/null +++ b/kogito-workitems/kogito-jackson-utils/src/main/java/org/kie/kogito/jackson/utils/CommonObjectModule.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.jackson.utils; + +import java.io.File; +import java.io.IOException; +import java.net.URI; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.module.SimpleModule; + +class CommonObjectModule extends SimpleModule { + + private static final long serialVersionUID = 1L; + + public CommonObjectModule() { + this.addDeserializer(URI.class, new JsonDeserializer() { + @Override + public URI deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return URI.create(fromNode(p)); + } + }); + + this.addDeserializer(File.class, new JsonDeserializer() { + @Override + public File deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return new File(fromNode(p)); + } + }); + } + + private static String fromNode(JsonParser p) throws IOException { + JsonNode node = p.readValueAsTree(); + if (node.size() == 1) { + node = node.iterator().next(); + } + if (node.isTextual()) { + return node.asText(); + } + throw new IOException(node + "should be a string or have exactly one property of type string"); + } +} diff --git a/kogito-workitems/kogito-jackson-utils/src/main/java/org/kie/kogito/jackson/utils/ObjectMapperFactory.java b/kogito-workitems/kogito-jackson-utils/src/main/java/org/kie/kogito/jackson/utils/ObjectMapperFactory.java index 747a0aaf054..18e13872c0a 100644 --- a/kogito-workitems/kogito-jackson-utils/src/main/java/org/kie/kogito/jackson/utils/ObjectMapperFactory.java +++ b/kogito-workitems/kogito-jackson-utils/src/main/java/org/kie/kogito/jackson/utils/ObjectMapperFactory.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -38,6 +39,8 @@ private static class DefaultObjectMapper { .setSerializationInclusion(JsonInclude.Include.NON_NULL) .setTypeFactory(TypeFactory.defaultInstance().withClassLoader(Thread.currentThread().getContextClassLoader())) .registerModule(JsonFormat.getCloudEventJacksonModule()) + .registerModule(new CommonObjectModule()) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .findAndRegisterModules(); } diff --git a/kogito-workitems/kogito-jackson-utils/src/test/java/org/kie/kogito/jackson/utils/JsonObjectUtilsTest.java b/kogito-workitems/kogito-jackson-utils/src/test/java/org/kie/kogito/jackson/utils/JsonObjectUtilsTest.java index f596a202af5..4976954db11 100644 --- a/kogito-workitems/kogito-jackson-utils/src/test/java/org/kie/kogito/jackson/utils/JsonObjectUtilsTest.java +++ b/kogito-workitems/kogito-jackson-utils/src/test/java/org/kie/kogito/jackson/utils/JsonObjectUtilsTest.java @@ -18,18 +18,23 @@ */ package org.kie.kogito.jackson.utils; +import java.io.File; +import java.net.URI; import java.util.Arrays; import java.util.Collections; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.node.BinaryNode; +import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.DoubleNode; import com.fasterxml.jackson.databind.node.FloatNode; import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.TextNode; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class JsonObjectUtilsTest { @@ -82,4 +87,69 @@ void testJavaByteArray() { byte[] bytes = { 1, 2, 3, 4 }; assertThat((byte[]) JsonObjectUtils.toJavaValue(BinaryNode.valueOf(bytes))).isEqualTo(bytes); } + + @Test + void testURI() { + final String uri = "www.google.com"; + assertThat(JsonObjectUtils.convertValue(ObjectMapperFactory.get().createObjectNode().put("uri", uri), URI.class)).isEqualTo(URI.create(uri)); + assertThat(JsonObjectUtils.convertValue(new TextNode(uri), URI.class)).isEqualTo(URI.create(uri)); + } + + @Test + void testFile() { + final String file = "/home/myhome/sample.txt"; + final String additionalData = "Javierito"; + assertThat(JsonObjectUtils.convertValue(ObjectMapperFactory.get().createObjectNode().put("file", file), File.class)).isEqualTo(new File(file)); + assertThat(JsonObjectUtils.convertValue(ObjectMapperFactory.get().createArrayNode().add(file), File.class)).isEqualTo(new File(file)); + assertThat(JsonObjectUtils.convertValue(ObjectMapperFactory.get().createObjectNode().put("file", file).put("additionalData", additionalData), PseudoPOJO.class)) + .isEqualTo(new PseudoPOJO(additionalData, new File(file))); + assertThat(JsonObjectUtils.convertValue(new TextNode(file), File.class)).isEqualTo(new File(file)); + } + + @Test + void testFileNullInput() { + assertThat(JsonObjectUtils.convertValue(NullNode.getInstance(), File.class)).isNull(); + } + + @Test + void testFileEmptyPath() { + final String emptyPath = ""; + assertThat(JsonObjectUtils.convertValue(new TextNode(emptyPath), File.class)).isEqualTo(new File(emptyPath)); + } + + @Test + void testFileWithSpecialCharacters() { + final String pathWithSpecialChars = "/home/user/my file en espaƱol.txt"; + assertThat(JsonObjectUtils.convertValue(new TextNode(pathWithSpecialChars), File.class)) + .isEqualTo(new File(pathWithSpecialChars)); + } + + @Test + void testUnsupportedNodeType() { + final String errorMessage = "should be a string or have exactly one property of type string"; + assertThatThrownBy(() -> JsonObjectUtils.convertValue(BooleanNode.TRUE, URI.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(errorMessage); + assertThatThrownBy(() -> JsonObjectUtils.convertValue(new IntNode(1), URI.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(errorMessage); + assertThatThrownBy(() -> JsonObjectUtils.convertValue(ObjectMapperFactory.get().createArrayNode(), URI.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(errorMessage); + assertThatThrownBy(() -> JsonObjectUtils.convertValue(ObjectMapperFactory.get().createObjectNode(), URI.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(errorMessage); + assertThatThrownBy(() -> JsonObjectUtils.convertValue(ObjectMapperFactory.get().createObjectNode().put("name", 1), URI.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(errorMessage); + assertThatThrownBy(() -> JsonObjectUtils.convertValue(ObjectMapperFactory.get().createArrayNode().add("first.txt").add("second.txt"), URI.class)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(errorMessage); + } + + private static record PseudoPOJO(String additionalData, File file) { + } + + private static record Person(String name) { + } } diff --git a/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRecorder.java b/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRecorder.java index 13d4c841610..f5dbb0dce88 100644 --- a/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRecorder.java +++ b/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRecorder.java @@ -19,9 +19,13 @@ package org.kie.flyway.quarkus; +import java.util.Collection; + import javax.sql.DataSource; +import org.kie.flyway.integration.KieFlywayNamedModule; import org.kie.flyway.integration.KieFlywayRunner; +import org.kie.flyway.integration.KieFlywayRunnerConfiguration; import io.quarkus.agroal.runtime.DataSources; import io.quarkus.arc.Arc; @@ -42,7 +46,15 @@ public void run(String defaultDSName) { DataSources agroalDatasourceS = Arc.container().select(DataSources.class).get(); DataSource dataSource = agroalDatasourceS.getDataSource(defaultDSName); - KieFlywayRunner.get(config.getValue()) + KieFlywayQuarkusRuntimeConfig runtimeConfig = config.getValue(); + + Collection kieFlywayNamedModules = runtimeConfig.modules.entrySet() + .stream() + .map(entry -> new KieFlywayNamedModule(entry.getKey(), entry.getValue().enabled)) + .toList(); + + KieFlywayRunnerConfiguration kieFlywayConfig = new KieFlywayRunnerConfiguration(runtimeConfig.enabled, kieFlywayNamedModules); + KieFlywayRunner.get(kieFlywayConfig) .runFlyway(dataSource); } } diff --git a/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRuntimeConfig.java b/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRuntimeConfig.java index 5819fa3a7cc..ae6e2ad70bc 100644 --- a/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRuntimeConfig.java +++ b/quarkus/addons/flyway/runtime/src/main/java/org/kie/flyway/quarkus/KieFlywayQuarkusRuntimeConfig.java @@ -21,16 +21,13 @@ import java.util.Map; -import org.kie.flyway.integration.KieFlywayConfiguration; -import org.kie.flyway.integration.KieFlywayNamedModule; - import io.quarkus.runtime.annotations.*; /** * Configuration for the Kie Flyway initializer */ @ConfigRoot(prefix = "kie", name = "flyway", phase = ConfigPhase.RUN_TIME) -public class KieFlywayQuarkusRuntimeConfig implements KieFlywayConfiguration { +public class KieFlywayQuarkusRuntimeConfig { /** * Enables the execution of the Flyway initializer during the application startup @@ -44,28 +41,13 @@ public class KieFlywayQuarkusRuntimeConfig implements KieFlywayConfiguration modules; - @Override - public boolean isEnabled() { - return enabled; - } - - @Override - public Map getModules() { - return modules; - } - @ConfigGroup - public static class KieQuarkusFlywayNamedModule implements KieFlywayNamedModule { + public static class KieQuarkusFlywayNamedModule { /** * Enables the execution of the Flyway initializer for a specific Kie module */ @ConfigItem(name = "enabled", defaultValue = "true") boolean enabled; - - @Override - public boolean isEnabled() { - return enabled; - } } } diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskDeadlineEntityMapper.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskDeadlineEntityMapper.java new file mode 100644 index 00000000000..3067cbb77fc --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskDeadlineEntityMapper.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskDeadlineEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskDeadlineRepository; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskDeadlineEntityMapper extends TaskDeadlineEntityMapper { + + public QuarkusTaskDeadlineEntityMapper() { + super(null); + } + + @Inject + public QuarkusTaskDeadlineEntityMapper(TaskDeadlineRepository repository) { + super(repository); + } +} diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskDeadlineTimerEntityMapper.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskDeadlineTimerEntityMapper.java new file mode 100644 index 00000000000..d7377affa74 --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskDeadlineTimerEntityMapper.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskDeadlineTimerEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskDeadlineTimerRepository; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskDeadlineTimerEntityMapper extends TaskDeadlineTimerEntityMapper { + + public QuarkusTaskDeadlineTimerEntityMapper() { + super(null); + } + + @Inject + public QuarkusTaskDeadlineTimerEntityMapper(TaskDeadlineTimerRepository repository) { + super(repository); + } +} diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskReassignmentEntityMapper.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskReassignmentEntityMapper.java new file mode 100644 index 00000000000..1d86a96522a --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskReassignmentEntityMapper.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskReassignmentEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskReassignmentRepository; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskReassignmentEntityMapper extends TaskReassignmentEntityMapper { + + public QuarkusTaskReassignmentEntityMapper() { + super(null); + } + + @Inject + public QuarkusTaskReassignmentEntityMapper(TaskReassignmentRepository repository) { + super(repository); + } +} diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskReassignmentTimerEntityMapper.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskReassignmentTimerEntityMapper.java new file mode 100644 index 00000000000..f5353c6ccb7 --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusTaskReassignmentTimerEntityMapper.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskReassignmentTimerEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskReassignmentTimerRepository; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskReassignmentTimerEntityMapper extends TaskReassignmentTimerEntityMapper { + + public QuarkusTaskReassignmentTimerEntityMapper() { + super(null); + } + + @Inject + public QuarkusTaskReassignmentTimerEntityMapper(TaskReassignmentTimerRepository repository) { + super(repository); + } +} diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusUserTaskInstanceEntityMapper.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusUserTaskInstanceEntityMapper.java index e3f6c79e1ad..372301f761e 100644 --- a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusUserTaskInstanceEntityMapper.java +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/mapper/QuarkusUserTaskInstanceEntityMapper.java @@ -19,21 +19,24 @@ package org.jbpm.usertask.jpa.quarkus.mapper; -import org.jbpm.usertask.jpa.mapper.*; +import java.util.Collections; + +import org.jbpm.usertask.jpa.mapper.EntityMapper; +import org.jbpm.usertask.jpa.mapper.UserTaskInstanceEntityMapper; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; @ApplicationScoped public class QuarkusUserTaskInstanceEntityMapper extends UserTaskInstanceEntityMapper { QuarkusUserTaskInstanceEntityMapper() { - this(null, null, null, null, null); + super(Collections.emptyList()); } @Inject - public QuarkusUserTaskInstanceEntityMapper(AttachmentsEntityMapper attachmentsMapper, CommentsEntityMapper commentsMapper, TaskMetadataEntityMapper taskMetadataEntityMapper, - TaskInputsEntityMapper taskInputsEntityMapper, TaskOutputsEntityMapper taskOutputsEntityMapper) { - super(attachmentsMapper, commentsMapper, taskMetadataEntityMapper, taskInputsEntityMapper, taskOutputsEntityMapper); + public QuarkusUserTaskInstanceEntityMapper(Instance mappers) { + super(mappers); } } diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskDeadlineRepository.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskDeadlineRepository.java new file mode 100644 index 00000000000..9cf4d5eb095 --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskDeadlineRepository.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.repository; + +import org.jbpm.usertask.jpa.repository.TaskDeadlineRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskDeadlineRepository extends TaskDeadlineRepository { + + QuarkusTaskDeadlineRepository() { + super(null); + } + + @Inject + public QuarkusTaskDeadlineRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskDeadlineTimerRepository.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskDeadlineTimerRepository.java new file mode 100644 index 00000000000..782b20a2932 --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskDeadlineTimerRepository.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.repository; + +import org.jbpm.usertask.jpa.repository.TaskDeadlineTimerRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskDeadlineTimerRepository extends TaskDeadlineTimerRepository { + + QuarkusTaskDeadlineTimerRepository() { + this(null); + } + + @Inject + public QuarkusTaskDeadlineTimerRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskReassignmentRepository.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskReassignmentRepository.java new file mode 100644 index 00000000000..e597dddc6c1 --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskReassignmentRepository.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.repository; + +import org.jbpm.usertask.jpa.repository.TaskReassignmentRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskReassignmentRepository extends TaskReassignmentRepository { + + QuarkusTaskReassignmentRepository() { + super(null); + } + + @Inject + public QuarkusTaskReassignmentRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskReassignmentTimerRepository.java b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskReassignmentTimerRepository.java new file mode 100644 index 00000000000..a92d3b6547e --- /dev/null +++ b/quarkus/addons/jbpm-usertask-storage-jpa/runtime/src/main/java/org/jbpm/usertask/jpa/quarkus/repository/QuarkusTaskReassignmentTimerRepository.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.quarkus.repository; + +import org.jbpm.usertask.jpa.repository.TaskReassignmentTimerRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class QuarkusTaskReassignmentTimerRepository extends TaskReassignmentTimerRepository { + + QuarkusTaskReassignmentTimerRepository() { + super(null); + } + + @Inject + public QuarkusTaskReassignmentTimerRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java b/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java index a2381404691..3f61e1fe1dd 100644 --- a/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java +++ b/quarkus/addons/jobs/common/messaging/src/main/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsService.java @@ -23,10 +23,9 @@ import org.eclipse.microprofile.reactive.messaging.Emitter; import org.eclipse.microprofile.reactive.messaging.Message; +import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.jobs.JobsService; import org.kie.kogito.jobs.JobsServiceException; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; -import org.kie.kogito.jobs.ProcessJobDescription; import org.kie.kogito.jobs.service.api.Job; import org.kie.kogito.jobs.service.api.JobLookupId; import org.kie.kogito.jobs.service.api.event.CreateJobEvent; @@ -73,12 +72,7 @@ protected AbstractReactiveMessagingJobsService(URI serviceUrl, } @Override - public String scheduleProcessJob(ProcessJobDescription description) { - throw new UnsupportedOperationException("Scheduling for process jobs is not yet implemented"); - } - - @Override - public String scheduleProcessInstanceJob(ProcessInstanceJobDescription description) { + public String scheduleJob(JobDescription description) { Job job = buildCallbackPatternJob(description, buildCallbackURI(description, serviceUrl.toString()), objectMapper); LOGGER.debug("scheduleProcessInstanceJob job: {}", job); CreateJobEvent event = CreateJobEvent.builder() diff --git a/quarkus/addons/jobs/common/messaging/src/test/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsServiceTest.java b/quarkus/addons/jobs/common/messaging/src/test/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsServiceTest.java index 3d22a9bfb19..ccb448a7af8 100644 --- a/quarkus/addons/jobs/common/messaging/src/test/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsServiceTest.java +++ b/quarkus/addons/jobs/common/messaging/src/test/java/org/kie/kogito/jobs/messaging/quarkus/AbstractReactiveMessagingJobsServiceTest.java @@ -29,10 +29,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.kie.kogito.jobs.ExactExpirationTime; import org.kie.kogito.jobs.ExpirationTime; +import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.jobs.JobsServiceException; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; -import org.kie.kogito.jobs.ProcessJobDescription; import org.kie.kogito.jobs.api.JobCallbackPayload; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.jobs.service.api.Job; import org.kie.kogito.jobs.service.api.JobLookupId; import org.kie.kogito.jobs.service.api.TemporalUnit; @@ -105,12 +105,12 @@ protected void setUp() { @Test protected void scheduleProcessInstanceJobSuccessful() throws Exception { - ProcessInstanceJobDescription description = mockProcessInstanceJobDescription(); + JobDescription description = mockProcessInstanceJobDescription(); CreateJobEvent expectedEvent = mockExpectedCreateJobEvent(); doReturn(SERIALIZED_EVENT).when(objectMapper).writeValueAsString(any(CreateJobEvent.class)); doReturn(JSON_PAYLOAD).when(objectMapper).valueToTree(any(JobCallbackPayload.class)); - jobsService.scheduleProcessInstanceJob(description); + jobsService.scheduleJob(description); verifyCreateJobEventWasCreated(1, expectedEvent); verifyEmitterWasInvoked(1, SERIALIZED_EVENT); @@ -137,7 +137,7 @@ protected void scheduleProcessInstanceJobWithFailureAndContinue() throws Excepti // Clear the errors and produce a second execution that must work fine. eventsEmitter.clearErrors(); doReturn(SERIALIZED_SECOND_EVENT).when(objectMapper).writeValueAsString(any(CreateJobEvent.class)); - jobsService.scheduleProcessInstanceJob(description); + jobsService.scheduleJob(description); verifyCreateJobEventWasCreated(2, expectedEvent, expectedEvent); verifyEmitterWasInvoked(2, SERIALIZED_EVENT, SERIALIZED_SECOND_EVENT); @@ -148,7 +148,7 @@ protected void executeScheduleProcessInstanceJobWithFailure(ProcessInstanceJobDe RuntimeException nackError = new RuntimeException(ERROR); eventsEmitter.setNackError(nackError); // ensure the execution failed as programmed - assertThatThrownBy(() -> jobsService.scheduleProcessInstanceJob(description)) + assertThatThrownBy(() -> jobsService.scheduleJob(description)) .isInstanceOf(JobsServiceException.class) .hasMessageContaining("Error while emitting JobCloudEvent") .hasCause(nackError); @@ -175,7 +175,7 @@ protected void scheduleProcessInstanceJobWithFatalFailureAndContinue() throws Ex // Clear the errors and produce a second execution that must work fine. eventsEmitter.clearErrors(); doReturn(SERIALIZED_SECOND_EVENT).when(objectMapper).writeValueAsString(any(CreateJobEvent.class)); - jobsService.scheduleProcessInstanceJob(description); + jobsService.scheduleJob(description); verifyCreateJobEventWasCreated(2, expectedEvent, expectedEvent); verifyEmitterWasInvoked(2, SERIALIZED_EVENT, SERIALIZED_SECOND_EVENT); @@ -186,7 +186,7 @@ protected void executeScheduleProcessInstanceJobWithFataFailure(ProcessInstanceJ RuntimeException fatalError = new RuntimeException(FATAL_ERROR); eventsEmitter.setFatalError(fatalError); // ensure the execution failed as programmed - assertThatThrownBy(() -> jobsService.scheduleProcessInstanceJob(description)) + assertThatThrownBy(() -> jobsService.scheduleJob(description)) .isInstanceOf(JobsServiceException.class) .hasMessageContaining("Error while emitting JobCloudEvent") .hasCause(fatalError); @@ -275,15 +275,8 @@ protected void executeCancelJobWithFatalFailure(String jobId) { .hasCause(fatalError); } - @Test - protected void scheduleProcessJob() { - ProcessJobDescription description = ProcessJobDescription.of(EXPIRATION_TIME, PROCESS_ID); - assertThatThrownBy(() -> jobsService.scheduleProcessJob(description)) - .isInstanceOf(UnsupportedOperationException.class); - } - protected ProcessInstanceJobDescription mockProcessInstanceJobDescription() { - return ProcessInstanceJobDescription.builder() + return ProcessInstanceJobDescription.newProcessInstanceJobDescriptionBuilder() .id(JOB_ID) .timerId(TIMER_ID) .expirationTime(EXPIRATION_TIME) diff --git a/quarkus/addons/jobs/common/rest-callback/src/main/java/org/kie/kogito/jobs/quarkus/common/CallbackJobsServiceResource.java b/quarkus/addons/jobs/common/rest-callback/src/main/java/org/kie/kogito/jobs/quarkus/common/CallbackJobsServiceResource.java index 0e678666198..cd4972bc630 100644 --- a/quarkus/addons/jobs/common/rest-callback/src/main/java/org/kie/kogito/jobs/quarkus/common/CallbackJobsServiceResource.java +++ b/quarkus/addons/jobs/common/rest-callback/src/main/java/org/kie/kogito/jobs/quarkus/common/CallbackJobsServiceResource.java @@ -22,6 +22,7 @@ import org.kie.kogito.Application; import org.kie.kogito.Model; +import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.jobs.api.JobCallbackPayload; import org.kie.kogito.process.Process; import org.kie.kogito.process.Processes; @@ -42,14 +43,13 @@ import jakarta.ws.rs.core.Response.Status; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.JOBS_CALLBACK_POST_URI; -import static org.kie.kogito.jobs.api.JobCallbackResourceDef.JOBS_CALLBACK_URI; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.LIMIT; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.LIMIT_DEFAULT_VALUE; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.PROCESS_ID; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.PROCESS_INSTANCE_ID; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.TIMER_ID; -@Path(JOBS_CALLBACK_URI) +@Path(JobDescription.JOBS_CALLBACK_URI) public class CallbackJobsServiceResource { @Inject diff --git a/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java b/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java index a7989885d0a..c642ff66745 100644 --- a/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java +++ b/quarkus/addons/jobs/management/runtime/src/main/java/org/kie/kogito/jobs/management/quarkus/VertxJobsService.java @@ -21,8 +21,7 @@ import java.net.URI; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; -import org.kie.kogito.jobs.ProcessJobDescription; +import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.jobs.management.RestJobsService; import org.kie.kogito.jobs.service.api.Job; import org.slf4j.Logger; @@ -89,13 +88,7 @@ void initialize() { } @Override - public String scheduleProcessJob(ProcessJobDescription description) { - - throw new UnsupportedOperationException("Scheduling for process jobs is not yet implemented"); - } - - @Override - public String scheduleProcessInstanceJob(ProcessInstanceJobDescription description) { + public String scheduleJob(JobDescription description) { String callback = getCallbackEndpoint(description); LOGGER.debug("Job to be scheduled {} with callback URL {}", description, callback); final Job job = buildJob(description, callback); diff --git a/quarkus/addons/jobs/management/runtime/src/test/java/org/kie/kogito/jobs/management/quarkus/VertxJobsServiceTest.java b/quarkus/addons/jobs/management/runtime/src/test/java/org/kie/kogito/jobs/management/quarkus/VertxJobsServiceTest.java index 578d7f954ca..56bc91ed720 100644 --- a/quarkus/addons/jobs/management/runtime/src/test/java/org/kie/kogito/jobs/management/quarkus/VertxJobsServiceTest.java +++ b/quarkus/addons/jobs/management/runtime/src/test/java/org/kie/kogito/jobs/management/quarkus/VertxJobsServiceTest.java @@ -20,8 +20,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; import org.kie.kogito.jobs.api.JobCallbackPayload; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.jobs.management.RestJobsServiceTest; import org.kie.kogito.jobs.service.api.Job; import org.mockito.ArgumentCaptor; @@ -87,7 +87,7 @@ void scheduleProcessInstanceJob() { when(webClient.post(anyString())).thenReturn(request); when(objectMapper.valueToTree(any(JobCallbackPayload.class))).thenReturn(JSON_PAYLOAD); ProcessInstanceJobDescription processInstanceJobDescription = buildProcessInstanceJobDescription(); - tested.scheduleProcessInstanceJob(processInstanceJobDescription); + tested.scheduleJob(processInstanceJobDescription); verify(webClient).post("/v2/jobs"); ArgumentCaptor jobArgumentCaptor = forClass(Job.class); verify(request).sendJson(jobArgumentCaptor.capture(), any(Handler.class)); diff --git a/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/src/main/java/org/kie/kogito/quarkus/workflow/KogitoBeanProducer.java b/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/src/main/java/org/kie/kogito/quarkus/workflow/KogitoBeanProducer.java index 7b282dc8f93..963f5958fc7 100644 --- a/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/src/main/java/org/kie/kogito/quarkus/workflow/KogitoBeanProducer.java +++ b/quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common/src/main/java/org/kie/kogito/quarkus/workflow/KogitoBeanProducer.java @@ -27,10 +27,13 @@ import org.kie.kogito.process.ProcessVersionResolver; import org.kie.kogito.process.Processes; import org.kie.kogito.process.version.ProjectVersionProcessVersionResolver; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; import org.kie.kogito.services.jobs.impl.InMemoryJobService; +import org.kie.kogito.services.jobs.impl.InMemoryProcessJobExecutorFactory; import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; import org.kie.kogito.uow.UnitOfWorkManager; +import org.kie.kogito.usertask.UserTasks; import io.quarkus.arc.DefaultBean; import io.quarkus.arc.properties.IfBuildProperty; @@ -61,8 +64,11 @@ UnitOfWorkManager unitOfWorkManager() { @DefaultBean @Produces - JobsService jobsService(Instance processes, UnitOfWorkManager uowm, ScheduledExecutorService executor) { - return InMemoryJobService.get(processes.isResolvable() ? processes.get() : null, uowm, executor); + JobsService jobsService(Instance processes, Instance userTasks, UnitOfWorkManager uowm, ScheduledExecutorService executor) { + InMemoryJobContext context = new InMemoryJobContext(null, uowm, processes.isResolvable() ? processes.get() : null, userTasks.isResolvable() ? userTasks.get() : null); + InMemoryJobService inMemoryJobService = new InMemoryJobService(executor); + inMemoryJobService.registerJobExecutorFactory(new InMemoryProcessJobExecutorFactory(context)); + return inMemoryJobService; } @Produces diff --git a/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootInitializer.java b/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootInitializer.java index dff89d58c2f..184a7f8c3f6 100644 --- a/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootInitializer.java +++ b/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootInitializer.java @@ -19,9 +19,13 @@ package org.kie.flyway.springboot; +import java.util.Collection; + import javax.sql.DataSource; +import org.kie.flyway.integration.KieFlywayNamedModule; import org.kie.flyway.integration.KieFlywayRunner; +import org.kie.flyway.integration.KieFlywayRunnerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -40,7 +44,13 @@ public KieFlywaySpringbootInitializer(KieFlywaySpringbootProperties properties, @Override public void afterPropertiesSet() { - KieFlywayRunner.get(properties) + + Collection kieFlywayNamedModules = properties.getModules().entrySet() + .stream() + .map(entry -> new KieFlywayNamedModule(entry.getKey(), entry.getValue().isEnabled())) + .toList(); + + KieFlywayRunner.get(new KieFlywayRunnerConfiguration(properties.isEnabled(), kieFlywayNamedModules)) .runFlyway(dataSource); } diff --git a/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootProperties.java b/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootProperties.java index 3c89ad447d6..c65da4a34bb 100644 --- a/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootProperties.java +++ b/springboot/addons/flyway/src/main/java/org/kie/flyway/springboot/KieFlywaySpringbootProperties.java @@ -22,13 +22,11 @@ import java.util.HashMap; import java.util.Map; -import org.kie.flyway.integration.KieFlywayConfiguration; -import org.kie.flyway.integration.KieFlywayNamedModule; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "kie.flyway") -public class KieFlywaySpringbootProperties implements KieFlywayConfiguration { - private boolean enabled = true; +public class KieFlywaySpringbootProperties { + private boolean enabled = false; private Map modules = new HashMap<>(); @@ -48,7 +46,7 @@ public void setModules(Map modules) { this.modules = modules; } - public static class KieFlywaySpringbootNamedModule implements KieFlywayNamedModule { + public static class KieFlywaySpringbootNamedModule { private boolean enabled = true; diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskDeadlineEntityMapper.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskDeadlineEntityMapper.java new file mode 100644 index 00000000000..03201da8fb0 --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskDeadlineEntityMapper.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskDeadlineEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskDeadlineRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskDeadlineEntityMapper extends TaskDeadlineEntityMapper { + + public SpringBootTaskDeadlineEntityMapper() { + super(null); + } + + @Autowired + public SpringBootTaskDeadlineEntityMapper(TaskDeadlineRepository repository) { + super(repository); + } +} diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskDeadlineTimerEntityMapper.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskDeadlineTimerEntityMapper.java new file mode 100644 index 00000000000..399a958431a --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskDeadlineTimerEntityMapper.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskDeadlineTimerEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskDeadlineTimerRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskDeadlineTimerEntityMapper extends TaskDeadlineTimerEntityMapper { + + public SpringBootTaskDeadlineTimerEntityMapper() { + super(null); + } + + @Autowired + public SpringBootTaskDeadlineTimerEntityMapper(TaskDeadlineTimerRepository repository) { + super(repository); + } +} diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskReassignmentEntityMapper.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskReassignmentEntityMapper.java new file mode 100644 index 00000000000..7e32aaee44f --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskReassignmentEntityMapper.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskReassignmentEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskReassignmentRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskReassignmentEntityMapper extends TaskReassignmentEntityMapper { + + public SpringBootTaskReassignmentEntityMapper() { + super(null); + } + + @Autowired + public SpringBootTaskReassignmentEntityMapper(TaskReassignmentRepository repository) { + super(repository); + } +} diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskReassignmentTimerEntityMapper.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskReassignmentTimerEntityMapper.java new file mode 100644 index 00000000000..47eb8f18008 --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootTaskReassignmentTimerEntityMapper.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.mapper; + +import org.jbpm.usertask.jpa.mapper.TaskReassignmentTimerEntityMapper; +import org.jbpm.usertask.jpa.repository.TaskReassignmentTimerRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskReassignmentTimerEntityMapper extends TaskReassignmentTimerEntityMapper { + + public SpringBootTaskReassignmentTimerEntityMapper() { + super(null); + } + + @Autowired + public SpringBootTaskReassignmentTimerEntityMapper(TaskReassignmentTimerRepository repository) { + super(repository); + } +} diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootUserTaskInstanceEntityMapper.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootUserTaskInstanceEntityMapper.java index 6c753b1b6a0..33462df7f05 100644 --- a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootUserTaskInstanceEntityMapper.java +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/mapper/SpringBootUserTaskInstanceEntityMapper.java @@ -19,7 +19,10 @@ package org.jbpm.usertask.jpa.springboot.mapper; -import org.jbpm.usertask.jpa.mapper.*; +import java.util.List; + +import org.jbpm.usertask.jpa.mapper.EntityMapper; +import org.jbpm.usertask.jpa.mapper.UserTaskInstanceEntityMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -27,8 +30,8 @@ public class SpringBootUserTaskInstanceEntityMapper extends UserTaskInstanceEntityMapper { @Autowired - public SpringBootUserTaskInstanceEntityMapper(AttachmentsEntityMapper attachmentsMapper, CommentsEntityMapper commentsMapper, TaskMetadataEntityMapper taskMetadataEntityMapper, - TaskInputsEntityMapper taskInputsEntityMapper, TaskOutputsEntityMapper taskOutputsEntityMapper) { - super(attachmentsMapper, commentsMapper, taskMetadataEntityMapper, taskInputsEntityMapper, taskOutputsEntityMapper); + public SpringBootUserTaskInstanceEntityMapper(List mappers) { + super(mappers); } + } diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskDeadlineRepository.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskDeadlineRepository.java new file mode 100644 index 00000000000..fe7f742cd4c --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskDeadlineRepository.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.repository; + +import org.jbpm.usertask.jpa.repository.TaskDeadlineRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskDeadlineRepository extends TaskDeadlineRepository { + + SpringBootTaskDeadlineRepository() { + super(null); + } + + @Autowired + public SpringBootTaskDeadlineRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskDeadlineTimerRepository.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskDeadlineTimerRepository.java new file mode 100644 index 00000000000..85a13568193 --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskDeadlineTimerRepository.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.repository; + +import org.jbpm.usertask.jpa.repository.TaskDeadlineTimerRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskDeadlineTimerRepository extends TaskDeadlineTimerRepository { + + SpringBootTaskDeadlineTimerRepository() { + this(null); + } + + @Autowired + public SpringBootTaskDeadlineTimerRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskReassignmentRepository.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskReassignmentRepository.java new file mode 100644 index 00000000000..1ccc6987f04 --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskReassignmentRepository.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.repository; + +import org.jbpm.usertask.jpa.repository.TaskReassignmentRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskReassignmentRepository extends TaskReassignmentRepository { + + SpringBootTaskReassignmentRepository() { + super(null); + } + + @Autowired + public SpringBootTaskReassignmentRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskReassignmentTimerRepository.java b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskReassignmentTimerRepository.java new file mode 100644 index 00000000000..076452807f0 --- /dev/null +++ b/springboot/addons/jbpm-usertask-storage-jpa/src/main/java/org/jbpm/usertask/jpa/springboot/repository/SpringBootTaskReassignmentTimerRepository.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jbpm.usertask.jpa.springboot.repository; + +import org.jbpm.usertask.jpa.repository.TaskReassignmentTimerRepository; +import org.jbpm.usertask.jpa.repository.UserTaskJPAContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SpringBootTaskReassignmentTimerRepository extends TaskReassignmentTimerRepository { + + SpringBootTaskReassignmentTimerRepository() { + super(null); + } + + @Autowired + public SpringBootTaskReassignmentTimerRepository(UserTaskJPAContext context) { + super(context); + } + +} diff --git a/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/CallbackJobsServiceResource.java b/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/CallbackJobsServiceResource.java index 752c6a84c70..438ac1659f1 100644 --- a/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/CallbackJobsServiceResource.java +++ b/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/CallbackJobsServiceResource.java @@ -39,8 +39,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; +import static org.kie.kogito.jobs.JobDescription.JOBS_CALLBACK_URI; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.JOBS_CALLBACK_POST_URI; -import static org.kie.kogito.jobs.api.JobCallbackResourceDef.JOBS_CALLBACK_URI; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.LIMIT; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.LIMIT_DEFAULT_VALUE; import static org.kie.kogito.jobs.api.JobCallbackResourceDef.PROCESS_ID; diff --git a/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java b/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java index ac20763e55c..b89392c51e2 100644 --- a/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java +++ b/springboot/addons/jobs/src/main/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsService.java @@ -18,8 +18,7 @@ */ package org.kie.kogito.jobs.management.springboot; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; -import org.kie.kogito.jobs.ProcessJobDescription; +import org.kie.kogito.jobs.JobDescription; import org.kie.kogito.jobs.management.RestJobsService; import org.kie.kogito.jobs.service.api.Job; import org.slf4j.Logger; @@ -73,13 +72,7 @@ public void initialize() { } @Override - public String scheduleProcessJob(ProcessJobDescription description) { - - throw new UnsupportedOperationException("Scheduling for process jobs is not yet implemented"); - } - - @Override - public String scheduleProcessInstanceJob(ProcessInstanceJobDescription description) { + public String scheduleJob(JobDescription description) { String callback = getCallbackEndpoint(description); LOGGER.debug("Job to be scheduled {} with callback URL {}", description, callback); final Job job = buildJob(description, callback); diff --git a/springboot/addons/jobs/src/test/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsServiceTest.java b/springboot/addons/jobs/src/test/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsServiceTest.java index 8804f608a8d..0daf9286806 100644 --- a/springboot/addons/jobs/src/test/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsServiceTest.java +++ b/springboot/addons/jobs/src/test/java/org/kie/kogito/jobs/management/springboot/SpringRestJobsServiceTest.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.kie.kogito.jobs.ProcessInstanceJobDescription; +import org.kie.kogito.jobs.descriptors.ProcessInstanceJobDescription; import org.kie.kogito.jobs.management.RestJobsServiceTest; import org.kie.kogito.jobs.service.api.Job; import org.mockito.ArgumentCaptor; @@ -63,7 +63,7 @@ public SpringRestJobsService createJobService(String jobServiceUrl, String callb void scheduleProcessInstanceJob() throws Exception { when(restTemplate.postForEntity(any(URI.class), any(HttpEntity.class), eq(String.class))).thenReturn(ResponseEntity.ok().build()); ProcessInstanceJobDescription processInstanceJobDescription = buildProcessInstanceJobDescription(); - tested.scheduleProcessInstanceJob(processInstanceJobDescription); + tested.scheduleJob(processInstanceJobDescription); ArgumentCaptor> jobArgumentCaptor = forClass(HttpEntity.class); verify(restTemplate).postForEntity(eq(tested.getJobsServiceUri()), jobArgumentCaptor.capture(), diff --git a/springboot/integration-tests/integration-tests-springboot-processes-persistence-it/integration-tests-springboot-processes-filesystem/.gitignore b/springboot/integration-tests/integration-tests-springboot-processes-persistence-it/integration-tests-springboot-processes-filesystem/.gitignore index a180e83ba79..c2ca403117d 100644 --- a/springboot/integration-tests/integration-tests-springboot-processes-persistence-it/integration-tests-springboot-processes-filesystem/.gitignore +++ b/springboot/integration-tests/integration-tests-springboot-processes-persistence-it/integration-tests-springboot-processes-filesystem/.gitignore @@ -19,3 +19,4 @@ *.bpmn *.wid +/target/ diff --git a/springboot/starters/kogito-processes-spring-boot-starter/src/main/java/org/kie/kogito/process/KogitoBeanProducer.java b/springboot/starters/kogito-processes-spring-boot-starter/src/main/java/org/kie/kogito/process/KogitoBeanProducer.java index d1875057a9d..1c65e780b99 100644 --- a/springboot/starters/kogito-processes-spring-boot-starter/src/main/java/org/kie/kogito/process/KogitoBeanProducer.java +++ b/springboot/starters/kogito-processes-spring-boot-starter/src/main/java/org/kie/kogito/process/KogitoBeanProducer.java @@ -18,13 +18,20 @@ */ package org.kie.kogito.process; +import java.util.List; + import org.kie.kogito.config.ConfigBean; import org.kie.kogito.correlation.CorrelationService; import org.kie.kogito.event.correlation.DefaultCorrelationService; +import org.kie.kogito.jobs.JobsService; import org.kie.kogito.process.version.ProjectVersionProcessVersionResolver; +import org.kie.kogito.services.jobs.impl.InMemoryJobContext; +import org.kie.kogito.services.jobs.impl.InMemoryJobService; +import org.kie.kogito.services.jobs.impl.InMemoryProcessJobExecutorFactory; import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; import org.kie.kogito.uow.UnitOfWorkManager; +import org.kie.kogito.usertask.UserTasks; import org.kogito.workitem.rest.RestWorkItemHandlerUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -50,6 +57,15 @@ CorrelationService correlationService() { return new DefaultCorrelationService(); } + @Bean + @ConditionalOnMissingBean(JobsService.class) + JobsService jobsService(List processes, List userTasks, UnitOfWorkManager uowm) { + InMemoryJobContext context = new InMemoryJobContext(null, uowm, !processes.isEmpty() ? processes.get(0) : null, !userTasks.isEmpty() ? userTasks.get(0) : null); + InMemoryJobService inMemoryJobService = new InMemoryJobService(); + inMemoryJobService.registerJobExecutorFactory(new InMemoryProcessJobExecutorFactory(context)); + return inMemoryJobService; + } + @Bean @ConditionalOnProperty(value = "kogito.workflow.version-strategy", havingValue = "project") ProcessVersionResolver projectVersionResolver() {