diff --git a/CHANGELOG.md b/CHANGELOG.md index 41afc2dee..7ad95fc77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,32 @@ **Highlights** - Use Spring Boot 4 in `nflow-examples` +- Dependency updates **Details** +- Dependency updates + - jersey 4.0.2 + - spring 7.0.9 + - jetty 12.1.12 + - apache cxf 4.2.3 + - jackson 2.22 + - jackson-jaxrs-json-provider 3.2.2 + - hibernate validator 9.1.3.Final + - logback 1.6.3 + - db2 jcc 12.1.5.0 + - mariadb 3.5.10 + - mysql-connector 26.7.0 + - ojdbc11 23.26.3.0.0 + - postgresql 42.7.13 + - HikariCP 7.1.0 + - joda-time 2.14.3 + - metrics 4.2.39 + - reactor-netty 1.3.7 + - swagger 2.2.54 + - asm 9.10.1 + - spotbugs 4.10.4 + ## 11.0.0 (2026-05-28) **Highlights** diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/DaoUtil.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/DaoUtil.java index 05b7eeb39..535f5b727 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/DaoUtil.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/DaoUtil.java @@ -1,5 +1,7 @@ package io.nflow.engine.internal.dao; +import static java.util.Optional.ofNullable; + import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -10,6 +12,7 @@ import org.joda.time.DateTime; import org.joda.time.base.BaseDateTime; import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.support.KeyHolder; public class DaoUtil { @@ -44,6 +47,11 @@ public static Long getLong(ResultSet rs, String columnLabel) throws SQLException return rs.wasNull() ? null : value; } + public static Number requireGeneratedKey(KeyHolder keyHolder, String operation) { + return ofNullable(keyHolder.getKey()) + .orElseThrow(() -> new IllegalStateException("Failed to " + operation + ": generated key was null")); + } + public static final class ColumnNamesExtractor implements ResultSetExtractor> { static final ColumnNamesExtractor columnNamesExtractor = new ColumnNamesExtractor(); diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/ExecutorDao.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/ExecutorDao.java index 3aa7dd8d7..b0978bbe9 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/ExecutorDao.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/ExecutorDao.java @@ -1,6 +1,7 @@ package io.nflow.engine.internal.dao; import static io.nflow.engine.internal.dao.DaoUtil.firstColumnLengthExtractor; +import static io.nflow.engine.internal.dao.DaoUtil.requireGeneratedKey; import static java.net.InetAddress.getLocalHost; import static java.util.Optional.ofNullable; import static org.apache.commons.lang3.StringUtils.left; @@ -144,7 +145,7 @@ public PreparedStatement createPreparedStatement(Connection con) throws SQLExcep return p; } }, keyHolder); - int allocatedExecutorId = keyHolder.getKey().intValue(); + int allocatedExecutorId = requireGeneratedKey(keyHolder, "allocate executor id").intValue(); logger.info("Joined executor group {} as executor {} running on host {} with process id {}.", executorGroup, allocatedExecutorId, host, pid); return allocatedExecutorId; diff --git a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java index e1631e475..dd979a58f 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java +++ b/nflow-engine/src/main/java/io/nflow/engine/internal/dao/WorkflowInstanceDao.java @@ -3,6 +3,7 @@ import static io.nflow.engine.internal.dao.DaoUtil.firstColumnLengthExtractor; import static io.nflow.engine.internal.dao.DaoUtil.getInt; import static io.nflow.engine.internal.dao.DaoUtil.getLong; +import static io.nflow.engine.internal.dao.DaoUtil.requireGeneratedKey; import static io.nflow.engine.internal.dao.DaoUtil.toTimestamp; import static io.nflow.engine.internal.dao.NflowTable.ACTION; import static io.nflow.engine.internal.dao.NflowTable.STATE; @@ -203,7 +204,8 @@ private long insertWorkflowInstanceWithCte(WorkflowInstance instance) { args[pos++] = variable.getValue(); } sqlb.append(" select wf.id from wf"); - return jdbc.queryForObject(sqlb.toString(), Long.class, args); + return ofNullable(jdbc.queryForObject(sqlb.toString(), Long.class, args)) + .orElseThrow(() -> new IllegalStateException("Failed to insert workflow instance: no id returned")); } catch (DuplicateKeyException e) { logger.warn("Failed to insert workflow instance", e); return -1; @@ -264,7 +266,7 @@ public PreparedStatement createPreparedStatement(Connection connection) throws S logger.warn("Failed to insert workflow instance", e); return -1L; } - long id = keyHolder.getKey().longValue(); + long id = requireGeneratedKey(keyHolder, "insert workflow instance").longValue(); insertVariables(id, 0, instance.stateVariables); return id; }); @@ -875,7 +877,7 @@ public PreparedStatement createPreparedStatement(Connection con) throws SQLExcep return p; } }, keyHolder); - return keyHolder.getKey().longValue(); + return requireGeneratedKey(keyHolder, "insert workflow action").longValue(); } public String getWorkflowInstanceState(long workflowInstanceId) { @@ -982,7 +984,9 @@ public boolean setSignal(long workflowInstanceId, Optional signal, Stri public String getWorkflowInstanceType(long workflowInstanceId) { String type = workflowTypeByWorkflowIdCache.computeIfAbsent(workflowInstanceId, - id -> jdbc.queryForObject("select type from nflow_workflow where id = ?", String.class, id).intern()); + id -> ofNullable(jdbc.queryForObject("select type from nflow_workflow where id = ?", String.class, id)) + .map(String::intern) + .orElseThrow(() -> new IllegalStateException("Workflow type not found for workflow id " + id))); if (workflowTypeByWorkflowIdCache.size() > workflowInstanceTypeCacheSize) { workflowTypeByWorkflowIdCache.clear(); } diff --git a/nflow-engine/src/main/java/io/nflow/engine/workflow/curated/CronWorkflow.java b/nflow-engine/src/main/java/io/nflow/engine/workflow/curated/CronWorkflow.java index acb6dbb03..7330c367f 100644 --- a/nflow-engine/src/main/java/io/nflow/engine/workflow/curated/CronWorkflow.java +++ b/nflow-engine/src/main/java/io/nflow/engine/workflow/curated/CronWorkflow.java @@ -6,6 +6,7 @@ import static io.nflow.engine.workflow.definition.WorkflowSettings.Builder.oncePerDay; import static io.nflow.engine.workflow.definition.WorkflowStateType.manual; import static io.nflow.engine.workflow.definition.WorkflowStateType.start; +import static java.util.Optional.ofNullable; import static org.joda.time.Days.days; import static org.slf4j.LoggerFactory.getLogger; @@ -117,7 +118,9 @@ public NextAction schedule(StateExecution execution, @StateVar(value = VAR_SCHED * @return The next activation time. */ protected DateTime getNextActivationTime(StateExecution execution, String cron) { - return new DateTime(CronExpression.parse(cron).next(ZonedDateTime.now()).toInstant().toEpochMilli()); + return ofNullable(CronExpression.parse(cron).next(ZonedDateTime.now())) + .map(next -> new DateTime(next.toInstant().toEpochMilli())) + .orElseThrow(() -> new IllegalArgumentException("Cron schedule has no future activations: " + cron)); } /** diff --git a/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowDefinitionServiceTest.java b/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowDefinitionServiceTest.java index 322d4bfe6..fd298fa76 100644 --- a/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowDefinitionServiceTest.java +++ b/nflow-engine/src/test/java/io/nflow/engine/service/WorkflowDefinitionServiceTest.java @@ -1,7 +1,6 @@ package io.nflow.engine.service; import static java.util.Collections.emptyList; -import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -29,6 +28,7 @@ import io.nflow.engine.internal.executor.BaseNflowTest; import java.util.List; +import java.lang.reflect.Field; public class WorkflowDefinitionServiceTest extends BaseNflowTest { @@ -162,7 +162,7 @@ public void getWorkflowDefinitionReturnsDefinitionWhenTypeIsFound() { @Test @SuppressWarnings("unchecked") public void getWorkflowDefinitionChecksFromDaoIfNotFoundFromMemory() throws Exception { - initializeService(true, true, 1); + initializeService(true, true, 60); service.addWorkflowDefinition(workflowDefinition); var w1 = new StoredWorkflowDefinition(); @@ -193,11 +193,17 @@ public void getWorkflowDefinitionChecksFromDaoIfNotFoundFromMemory() throws Exce assertThat(service.getWorkflowDefinition("w2"), is(nullValue())); verify(workflowDefinitionDao, times(1)).queryStoredWorkflowDefinitions(emptyList()); - SECONDS.sleep(2); + forceStoredDefinitionRefresh(service); // after timeout fetching existing will refresh assertThat(service.getWorkflowDefinition("w1"), is(notNullValue())); verify(workflowDefinitionDao, times(2)).queryStoredWorkflowDefinitions(emptyList()); } + private static void forceStoredDefinitionRefresh(WorkflowDefinitionService service) throws Exception { + Field nextCheck = WorkflowDefinitionService.class.getDeclaredField("nextCheckOfStoredDefinitions"); + nextCheck.setAccessible(true); + nextCheck.setLong(service, 0); + } + } diff --git a/nflow-jetty/src/main/java/io/nflow/jetty/servlet/MetricsServletContextListener.java b/nflow-jetty/src/main/java/io/nflow/jetty/servlet/MetricsServletContextListener.java index d1bd98c38..19fe1f91e 100644 --- a/nflow-jetty/src/main/java/io/nflow/jetty/servlet/MetricsServletContextListener.java +++ b/nflow-jetty/src/main/java/io/nflow/jetty/servlet/MetricsServletContextListener.java @@ -1,6 +1,6 @@ package io.nflow.jetty.servlet; -import static org.springframework.web.context.support.WebApplicationContextUtils.findWebApplicationContext; +import static org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext; import io.dropwizard.metrics.servlets.HealthCheckServlet; import io.dropwizard.metrics.servlets.MetricsServlet; @@ -24,7 +24,7 @@ public void contextInitialized(ServletContextEvent servletContextEvent) { } private T getSpringBean(Class clazz, ServletContext context) { - return findWebApplicationContext(context).getBean(clazz); + return getRequiredWebApplicationContext(context).getBean(clazz); } @Override diff --git a/nflow-rest-api-jax-rs/pom.xml b/nflow-rest-api-jax-rs/pom.xml index 58d3ed2a5..56a3e5025 100644 --- a/nflow-rest-api-jax-rs/pom.xml +++ b/nflow-rest-api-jax-rs/pom.xml @@ -80,7 +80,7 @@ org.glassfish.jersey.containers - jersey-container-simple-http + jersey-container-jdk-http test diff --git a/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/JerseyCompabilityTest.java b/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/JerseyCompabilityTest.java index 434a2fd99..8fa47fa57 100644 --- a/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/JerseyCompabilityTest.java +++ b/nflow-rest-api-jax-rs/src/test/java/io/nflow/rest/JerseyCompabilityTest.java @@ -2,14 +2,14 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; -import java.io.IOException; import java.net.URI; +import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory; import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.simple.SimpleContainerFactory; -import org.glassfish.jersey.simple.SimpleServer; import org.junit.jupiter.api.Test; +import com.sun.net.httpserver.HttpServer; + import io.nflow.rest.config.jaxrs.DateTimeParamConverterProvider; import io.nflow.rest.v1.jaxrs.MaintenanceResource; import jakarta.ws.rs.core.UriBuilder; @@ -17,11 +17,14 @@ public class JerseyCompabilityTest { @Test - public void restApiWorksInJersey() throws IOException { + public void restApiWorksInJersey() { URI baseUri = UriBuilder.fromUri("http://localhost/").port(0).build(); ResourceConfig config = new JerseyResourceConfig(); - try (SimpleServer server = SimpleContainerFactory.create(baseUri, config)) { - assertNotEquals(0, server.getPort()); + HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config); + try { + assertNotEquals(0, server.getAddress().getPort()); + } finally { + server.stop(0); } } diff --git a/nflow-tests/src/test/java/io/nflow/tests/ActionStateVariablesTest.java b/nflow-tests/src/test/java/io/nflow/tests/ActionStateVariablesTest.java index 263b02cea..b5c59c071 100644 --- a/nflow-tests/src/test/java/io/nflow/tests/ActionStateVariablesTest.java +++ b/nflow-tests/src/test/java/io/nflow/tests/ActionStateVariablesTest.java @@ -48,7 +48,7 @@ public void createWorkflow() { createRequest = new CreateWorkflowInstanceRequest(); createRequest.type = ActionStateVariableWorkflow.WORKFLOW_TYPE; createRequest.externalId = randomUUID().toString(); - createResponse = assertTimeoutPreemptively(ofSeconds(5), () -> createWorkflowInstance(createRequest)); + createResponse = assertTimeoutPreemptively(ofSeconds(10), () -> createWorkflowInstance(createRequest)); assertThat(createResponse.id, notNullValue()); } diff --git a/pom.xml b/pom.xml index 0bcc37740..6b7306fb0 100644 --- a/pom.xml +++ b/pom.xml @@ -109,25 +109,25 @@ 33.6.0-jre 1C - 9.10 - 4.2.1 + 9.10.1 + 4.2.3 3.6.1 - 0.10.0 + 0.11.0 3.20.0 1.4 4.3.0 - 12.1.4.0 + 12.1.5.0 4.0.2 - 2.0.0 + 2.0.2 7.0.0 2.4.240 3.0 - 9.1.0.Final - 7.0.2 - 2.21 - 3.1.3 - 3.1.3 - 3.1.3 + 9.1.3.Final + 7.1.0 + 2.22 + 3.2.2 + 3.2.2 + 3.2.2 3.31.0-GA 3.0.0 6.1.0 @@ -135,12 +135,12 @@ 4.0.0 4.1.0-M1 4.0.3 - 3.1.11 - 12.1.9 - 2.14.2 - 6.1.0 - 1.5.32 - 3.5.8 + 4.0.2 + 12.1.12 + 2.14.3 + 6.1.3 + 1.6.3 + 3.5.10 3.8.0 3.5.0 3.15.0 @@ -150,8 +150,8 @@ 3.6.3 3.2.8 3.1.4 - 0.8.14 - 3.5.0 + 0.8.15 + 3.5.1 3.12.0 3.6.0 3.28.0 @@ -162,27 +162,27 @@ 4.0.0-M16 3.4.0 3.5.5 - 4.2.38 + 4.2.39 5.23.0 13.4.0.jre11 - 9.7.0 + 26.7.0 1.7.0 v26.1.0 11.1.2 1.3 - 23.26.2.0.0 - 42.7.11 + 23.26.3.0.0 + 42.7.13 UTF-8 UTF-8 3.8.5 - 1.3.5 + 1.3.7 0.10.2 2.0.18 - 4.9.8 + 4.10.4 7.6.4 - 4.9.8.3 - 7.0.7 - 2.2.50 + 4.10.4.0 + 7.0.9 + 2.2.54 3.1.1 2.21.0 @@ -416,7 +416,7 @@ - 3.8.8 + 3.9.11 ${jdk.version} @@ -980,7 +980,7 @@ org.glassfish.jersey.containers - jersey-container-simple-http + jersey-container-jdk-http ${jersey.version} test