Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {

Expand Down Expand Up @@ -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<List<String>> {
static final ColumnNamesExtractor columnNamesExtractor = new ColumnNamesExtractor();

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
});
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -982,7 +984,9 @@ public boolean setSignal(long workflowInstanceId, Optional<Integer> 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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -24,7 +24,7 @@ public void contextInitialized(ServletContextEvent servletContextEvent) {
}

private <T> T getSpringBean(Class<T> clazz, ServletContext context) {
return findWebApplicationContext(context).getBean(clazz);
return getRequiredWebApplicationContext(context).getBean(clazz);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion nflow-rest-api-jax-rs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-simple-http</artifactId>
<artifactId>jersey-container-jdk-http</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,29 @@

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;

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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down
60 changes: 30 additions & 30 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,38 +109,38 @@
<java26ExtraArgs></java26ExtraArgs>
<guava.version>33.6.0-jre</guava.version>
<surefire.forkcount>1C</surefire.forkcount>
<asm.version>9.10</asm.version>
<apache.cxf.version>4.2.1</apache.cxf.version>
<asm.version>9.10.1</asm.version>
<apache.cxf.version>4.2.3</apache.cxf.version>
<build-helper.version>3.6.1</build-helper.version>
<central-publishing-maven-plugin.version>0.10.0</central-publishing-maven-plugin.version>
<central-publishing-maven-plugin.version>0.11.0</central-publishing-maven-plugin.version>
<commons.lang3.version>3.20.0</commons.lang3.version>
<core-utils.version>1.4</core-utils.version>
<coveralls.version>4.3.0</coveralls.version>
<db2-jcc.version>12.1.4.0</db2-jcc.version>
<db2-jcc.version>12.1.5.0</db2-jcc.version>
<el.version>4.0.2</el.version>
<frontend-maven.version>2.0.0</frontend-maven.version>
<frontend-maven.version>2.0.2</frontend-maven.version>
<guice.version>7.0.0</guice.version>
<h2.version>2.4.240</h2.version>
<hamcrest.version>3.0</hamcrest.version>
<hibernate.validator.version>9.1.0.Final</hibernate.validator.version>
<hikaricp.version>7.0.2</hikaricp.version>
<jackson.version>2.21</jackson.version>
<jackson-databind.version>3.1.3</jackson-databind.version>
<jackson-joda.version>3.1.3</jackson-joda.version>
<jackson.rsjson.version>3.1.3</jackson.rsjson.version>
<hibernate.validator.version>9.1.3.Final</hibernate.validator.version>
<hikaricp.version>7.1.0</hikaricp.version>
<jackson.version>2.22</jackson.version>
<jackson-databind.version>3.2.2</jackson-databind.version>
<jackson-joda.version>3.2.2</jackson-joda.version>
<jackson.rsjson.version>3.2.2</jackson.rsjson.version>
<javassist.version>3.31.0-GA</javassist.version>
<jakarta.annotation-api.version>3.0.0</jakarta.annotation-api.version>
<jakarta.servlet-api.version>6.1.0</jakarta.servlet-api.version>
<jakarta.inject-api.version>2.0.1</jakarta.inject-api.version>
<jakarta.ws.rs.version>4.0.0</jakarta.ws.rs.version>
<jakarta.xml.bind-api.version>4.1.0-M1</jakarta.xml.bind-api.version>
<jaxws-api.version>4.0.3</jaxws-api.version>
<jersey.version>3.1.11</jersey.version>
<jetty.version>12.1.9</jetty.version>
<jodatime.version>2.14.2</jodatime.version>
<junit5.version>6.1.0</junit5.version>
<logback-classic.version>1.5.32</logback-classic.version>
<mariadb.version>3.5.8</mariadb.version>
<jersey.version>4.0.2</jersey.version>
<jetty.version>12.1.12</jetty.version>
<jodatime.version>2.14.3</jodatime.version>
<junit5.version>6.1.3</junit5.version>
<logback-classic.version>1.6.3</logback-classic.version>
<mariadb.version>3.5.10</mariadb.version>
<maven-assembly.version>3.8.0</maven-assembly.version>
<maven-clean.version>3.5.0</maven-clean.version>
<maven-compiler.version>3.15.0</maven-compiler.version>
Expand All @@ -150,8 +150,8 @@
<maven-enforcer.version>3.6.3</maven-enforcer.version>
<maven-gpg.version>3.2.8</maven-gpg.version>
<maven-install.version>3.1.4</maven-install.version>
<maven-jacoco.version>0.8.14</maven-jacoco.version>
<maven-jar.version>3.5.0</maven-jar.version>
<maven-jacoco.version>0.8.15</maven-jacoco.version>
<maven-jar.version>3.5.1</maven-jar.version>
<maven-javadoc.version>3.12.0</maven-javadoc.version>
<maven-jxr.version>3.6.0</maven-jxr.version>
<maven-pmd.version>3.28.0</maven-pmd.version>
Expand All @@ -162,27 +162,27 @@
<maven-site.version>4.0.0-M16</maven-site.version>
<maven-source.version>3.4.0</maven-source.version>
<maven-surefire.version>3.5.5</maven-surefire.version>
<metrics.version>4.2.38</metrics.version>
<metrics.version>4.2.39</metrics.version>
<mockito.version>5.23.0</mockito.version>
<mssql.version>13.4.0.jre11</mssql.version>
<mysql.version>9.7.0</mysql.version>
<mysql.version>26.7.0</mysql.version>
<nexus-staging-maven.version>1.7.0</nexus-staging-maven.version>
<node.version>v26.1.0</node.version> <!-- https://nodejs.org/en/download/releases/ -->
<pnpm.version>11.1.2</pnpm.version> <!-- https://github.com/pnpm/pnpm/releases -->
<nitor-matchers.version>1.3</nitor-matchers.version>
<ojdbc.version>23.26.2.0.0</ojdbc.version>
<postgresql.version>42.7.11</postgresql.version>
<ojdbc.version>23.26.3.0.0</ojdbc.version>
<postgresql.version>42.7.13</postgresql.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<reactor.core.version>3.8.5</reactor.core.version>
<reactor.netty.version>1.3.5</reactor.netty.version>
<reactor.netty.version>1.3.7</reactor.netty.version>
<reflections.version>0.10.2</reflections.version>
<slf4j.version>2.0.18</slf4j.version>
<spotbugs.version>4.9.8</spotbugs.version>
<spotbugs.version>4.10.4</spotbugs.version>
<spotbugs-contrib.version>7.6.4</spotbugs-contrib.version>
<spotbugs-maven-plugin.version>4.9.8.3</spotbugs-maven-plugin.version>
<spring.version>7.0.7</spring.version>
<swagger.version>2.2.50</swagger.version>
<spotbugs-maven-plugin.version>4.10.4.0</spotbugs-maven-plugin.version>
<spring.version>7.0.9</spring.version>
<swagger.version>2.2.54</swagger.version>
<validation.api.version>3.1.1</validation.api.version>
<versions-maven-plugin.version>2.21.0</versions-maven-plugin.version>
</properties>
Expand Down Expand Up @@ -416,7 +416,7 @@
</excludes>
</bannedDependencies>
<requireMavenVersion>
<version>3.8.8</version>
<version>3.9.11</version>
</requireMavenVersion>
<requireJavaVersion>
<version>${jdk.version}</version>
Expand Down Expand Up @@ -980,7 +980,7 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-simple-http</artifactId>
<artifactId>jersey-container-jdk-http</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
Expand Down
Loading