diff --git a/bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaEngine.java b/bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaEngine.java
index b9233db3862..6985ce07836 100644
--- a/bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaEngine.java
+++ b/bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaEngine.java
@@ -115,7 +115,7 @@ private void initializeJNDI() throws NamingException {
public void start() throws Exception {
initializeEnvironment();
- PlatformSetup platformSetup = PlatformSetupAccessor.getPlatformSetup();
+ PlatformSetup platformSetup = getPlatformSetup();
platformSetup.init();
PlatformSession platformSession = loginOnPlatform();
@@ -126,6 +126,10 @@ public void start() throws Exception {
logoutFromPlatform(platformSession);
}
+ protected PlatformSetup getPlatformSetup() throws NamingException {
+ return PlatformSetupAccessor.getInstance().getPlatformSetup();
+ }
+
private void logoutFromPlatform(PlatformSession platformSession)
throws PlatformLogoutException, SessionNotFoundException, BonitaHomeNotSetException, ServerAPIException,
UnknownAPITypeException {
diff --git a/bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/CommonAPIIT.java b/bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/CommonAPIIT.java
index cb0b23b3240..af2866c2475 100644
--- a/bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/CommonAPIIT.java
+++ b/bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/CommonAPIIT.java
@@ -60,10 +60,6 @@ public void clean() throws Exception {
}
};
- /**
- * @return warning list of unclean elements
- * @throws BonitaException
- */
private void clean() throws BonitaException {
loginOnDefaultTenantWithDefaultTechnicalUser();
cleanCommands();
diff --git a/bonita-integration-tests/bonita-integration-tests-client/src/test/resources/logback-test.xml b/bonita-integration-tests/bonita-integration-tests-client/src/test/resources/logback-test.xml
index 33746fcdf05..06243d23e57 100644
--- a/bonita-integration-tests/bonita-integration-tests-client/src/test/resources/logback-test.xml
+++ b/bonita-integration-tests/bonita-integration-tests-client/src/test/resources/logback-test.xml
@@ -10,6 +10,7 @@
+
diff --git a/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/JobExecutionIT.java b/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/JobExecutionIT.java
index b10b2dbc52e..8e6f1931a06 100644
--- a/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/JobExecutionIT.java
+++ b/bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/JobExecutionIT.java
@@ -19,9 +19,7 @@
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.Assert.assertEquals;
-import java.io.Serializable;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -147,13 +145,12 @@ private List searchJobDescriptors(final int nbOfExpectedJobDescr
public void retryAJob_should_update_job_log_when_execution_fails_again() throws Exception {
//given
getCommandAPI().register("except", "Throws Exception when scheduling a job", AddJobCommand.class.getName());
- final Map parameters = new HashMap<>();
try {
- getCommandAPI().execute("except", parameters);
+ getCommandAPI().execute("except", Map.of());
FailedJob failedJob = await().until(() -> getProcessAPI().getFailedJobs(0, 100), hasSize(1)).get(0);
//when
- getProcessAPI().replayFailedJob(failedJob.getJobDescriptorId(), emptyMap());
+ getProcessAPI().replayFailedJob(failedJob.getJobDescriptorId());
//then
failedJob = await().until(() -> getProcessAPI().getFailedJobs(0, 100), hasSize(1)).get(0);
diff --git a/bonita-integration-tests/bonita-test-utils/build.gradle b/bonita-integration-tests/bonita-test-utils/build.gradle
index 9fc8e92ec88..f745de90326 100644
--- a/bonita-integration-tests/bonita-test-utils/build.gradle
+++ b/bonita-integration-tests/bonita-test-utils/build.gradle
@@ -6,6 +6,9 @@ dependencies {
api libs.commonsIO
api "xmlunit:xmlunit:${Deps.xmlunitVersion}"
api "org.assertj:assertj-core:${Deps.assertjVersion}"
+
+ implementation project(':bpm:bonita-core:bonita-process-engine')
+ implementation libs.springTest
}
publishing {
diff --git a/bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/EventUtil.java b/bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/EventUtil.java
deleted file mode 100644
index cecd29a0fdb..00000000000
--- a/bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/EventUtil.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * Copyright (C) 2019 Bonitasoft S.A.
- * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
- * This library is free software; you can redistribute it and/or modify it under the terms
- * of the GNU Lesser General Public License as published by the Free Software Foundation
- * version 2.1 of the License.
- * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- * You should have received a copy of the GNU Lesser General Public License along with this
- * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
- * Floor, Boston, MA 02110-1301, USA.
- **/
-package org.bonitasoft.engine.test;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeoutException;
-
-import org.bonitasoft.engine.api.CommandAPI;
-import org.bonitasoft.engine.api.TenantAPIAccessor;
-import org.bonitasoft.engine.command.CommandExecutionException;
-import org.bonitasoft.engine.command.CommandNotFoundException;
-import org.bonitasoft.engine.command.CommandParameterizationException;
-import org.bonitasoft.engine.exception.BonitaException;
-import org.bonitasoft.engine.exception.RetrieveException;
-import org.bonitasoft.engine.io.IOUtil;
-import org.bonitasoft.engine.session.APISession;
-
-/**
- * Utility methods to get maps that match process as wanted
- *
- * @author Baptiste Mesta
- */
-public class EventUtil {
-
- private static final String FLOW_NODE = "flowNode";
-
- private static final String PROCESS = "process";
-
- private static final String TYPE = "type";
-
- private static final String NAME = "name";
-
- private static final String ROOT_CONTAINER_ID = "rootContainerId";
-
- private static final String PARENT_CONTAINER_ID = "parentContainerId";
-
- private static final String STATE_ID = "stateId";
-
- private static final String ID = "id";
-
- private static final String STATE = "state";
-
- public static Map getReadyTaskEvent(final long processInstanceId, final String taskName) {
- final Map map = new HashMap(5);
- map.put(TYPE, FLOW_NODE);
- map.put(ROOT_CONTAINER_ID, processInstanceId);
- map.put(NAME, taskName);
- map.put(STATE_ID, 4);
- return map;
- }
-
- public static Map getProcessInstanceFinishedEvent(final long processInstanceId) {
- final Map map = new HashMap(4);
- map.put(TYPE, PROCESS);
- map.put(ID, processInstanceId);
- map.put(STATE_ID, 6);
- return map;
- }
-
- public static void undeployCommand(final APISession apiSession) throws BonitaException {
- final CommandAPI commandAPI = TenantAPIAccessor.getCommandAPI(apiSession);
- commandAPI.unregister("waitServerCommand");
- commandAPI.removeDependency("commands");
- }
-
- public static void deployCommand(final APISession apiSession) throws BonitaException {
- final CommandAPI commandAPI = TenantAPIAccessor.getCommandAPI(apiSession);
-
- byte[] commandJar;
- try {
- commandJar = IOUtil.getAllContentFrom(APITestUtil.class.getResourceAsStream("/server-command.bak"));
- } catch (final IOException e) {
- throw new RetrieveException(e);
- }
- commandAPI.addDependency("commands", commandJar);
-
- commandAPI.register("waitServerCommand", "waitServerCommand",
- "org.bonitasoft.engine.test.synchro.WaitServerCommand");
- commandAPI.register("addHandlerCommand", "addHandlerCommand",
- "org.bonitasoft.engine.test.synchro.AddHandlerCommand");
-
- final Map parameters = Collections.emptyMap();
- commandAPI.execute("addHandlerCommand", parameters);
- }
-
- static Long executeWaitServerCommand(final CommandAPI commandAPI, final Map event,
- final int defaultTimeout)
- throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException,
- TimeoutException {
- final Map parameters = new HashMap(2);
- parameters.put("event", (Serializable) event);
- parameters.put("timeout", defaultTimeout);
- try {
- return (Long) commandAPI.execute("waitServerCommand", parameters);
- } catch (final CommandExecutionException e) {
- if (e.getMessage().toLowerCase().contains("timeout")) {
- throw new TimeoutException("Timeout (" + defaultTimeout + "ms)looking for element: " + event);
- }
- throw e;
- }
- }
-
- public static void clearRepo(final CommandAPI commandAPI) throws BonitaException {
- final Map parameters = new HashMap();
- parameters.put("clear", true);
- commandAPI.execute("waitServerCommand", parameters);
- }
-
- public static Map getTaskInState(final long processInstanceId, final String stateName) {
- final Map map = new HashMap(3);
- map.put(TYPE, FLOW_NODE);
- map.put(ROOT_CONTAINER_ID, processInstanceId);
- map.put(STATE, stateName);
- return map;
- }
-
- public static Map getTaskInState(final long processInstanceId, final String state,
- final String flowNodeName) {
- final Map map = new HashMap(3);
- map.put(TYPE, FLOW_NODE);
- map.put(ROOT_CONTAINER_ID, processInstanceId);
- map.put(STATE, state);
- map.put(NAME, flowNodeName);
- return map;
- }
-
- public static Map getTaskInStateWithParentId(final long processInstanceId, final String state,
- final String flowNodeName) {
- final Map map = new HashMap(3);
- map.put(TYPE, FLOW_NODE);
- map.put(PARENT_CONTAINER_ID, processInstanceId);
- map.put(STATE, state);
- map.put(NAME, flowNodeName);
- return map;
- }
-
-}
diff --git a/bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/PlatformTestUtil.java b/bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/PlatformTestUtil.java
index 90c86e66b8a..7ab58df7664 100644
--- a/bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/PlatformTestUtil.java
+++ b/bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/PlatformTestUtil.java
@@ -23,11 +23,16 @@
import org.bonitasoft.engine.exception.BonitaHomeNotSetException;
import org.bonitasoft.engine.exception.ServerAPIException;
import org.bonitasoft.engine.exception.UnknownAPITypeException;
+import org.bonitasoft.engine.execution.ProcessStarterVerifier;
import org.bonitasoft.engine.session.PlatformSession;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
/**
* @author Celine Souchet
*/
+@ContextConfiguration(classes = PlatformTestUtil.TestConfiguration.class)
public class PlatformTestUtil {
public static final String DEFAULT_TECHNICAL_LOGGER_USERNAME = "install";
@@ -72,4 +77,17 @@ public void deployCommandsOnDefaultTenant() throws BonitaException {
apiClient.logout();
}
+ /**
+ * Configuration class used to override bean definitions for test purposes.
+ */
+ @Configuration
+ static class TestConfiguration {
+
+ @Bean
+ ProcessStarterVerifier processStarterVerifierImpl() {
+ return processInstance -> {
+ // Override this bean to disable the process starter verifier
+ };
+ }
+ }
}
diff --git a/bonita-test-api/build.gradle b/bonita-test-api/build.gradle
index 2636a0b4a49..62e8a3ada06 100644
--- a/bonita-test-api/build.gradle
+++ b/bonita-test-api/build.gradle
@@ -4,6 +4,8 @@ dependencies {
api "junit:junit:${Deps.junit4Version}"
api(project(':platform:platform-resources'))
+ implementation libs.springTest
+
// for http tests:
compileOnly("org.eclipse.jetty:jetty-server:${Deps.jettyVersion}")
compileOnly("org.eclipse.jetty:jetty-servlet:${Deps.jettyVersion}")
diff --git a/bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngineImpl.java b/bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngineImpl.java
index 80b4d0c8815..010e8d4725a 100644
--- a/bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngineImpl.java
+++ b/bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngineImpl.java
@@ -16,15 +16,20 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.bonitasoft.engine.BonitaDatabaseConfiguration;
+import org.bonitasoft.engine.execution.ProcessStarterVerifier;
import org.bonitasoft.engine.test.http.BonitaHttpServer;
import org.bonitasoft.engine.test.internal.EngineCommander;
import org.bonitasoft.engine.test.internal.EngineStarter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
/**
* @author Baptiste Mesta
*/
@Getter
@Slf4j
+@ContextConfiguration(classes = TestEngineImpl.TestConfiguration.class)
public class TestEngineImpl implements TestEngine {
private static TestEngineImpl INSTANCE = createTestEngine();
@@ -129,4 +134,17 @@ public void setBusinessDataDatabaseProperties(BonitaDatabaseConfiguration databa
this.businessDataDatabaseConfiguration = database;
}
+ /**
+ * Configuration class used to override bean definitions for test purposes.
+ */
+ @Configuration
+ static class TestConfiguration {
+
+ @Bean
+ ProcessStarterVerifier processStarterVerifierImpl() {
+ return processInstance -> {
+ // Override this bean to disable the process starter verifier
+ };
+ }
+ }
}
diff --git a/bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineStarter.java b/bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineStarter.java
index 294f2474c5a..608a22c3901 100644
--- a/bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineStarter.java
+++ b/bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineStarter.java
@@ -21,6 +21,8 @@
import java.nio.file.Path;
import java.util.*;
+import javax.naming.NamingException;
+
import org.apache.commons.io.FileUtils;
import org.bonitasoft.engine.BonitaDatabaseConfiguration;
import org.bonitasoft.engine.BonitaEngine;
@@ -56,7 +58,7 @@ public class EngineStarter {
private static boolean hasFailed = false;
private boolean dropOnStart = true;
- private BonitaEngine engine;
+ private final BonitaEngine engine;
protected EngineStarter(BonitaEngine engine) {
this.engine = engine;
@@ -76,7 +78,7 @@ public void start() throws Exception {
}
try {
LOGGER.info("=====================================================");
- LOGGER.info("============ Starting Bonita Engine ===========");
+ LOGGER.info("============== Starting Bonita Engine =============");
LOGGER.info("=====================================================");
final long startTime = System.currentTimeMillis();
System.setProperty("com.arjuna.ats.arjuna.common.propertiesFile", "jbossts-properties.xml");
@@ -102,12 +104,16 @@ public void start() throws Exception {
}
protected void setupPlatform() throws Exception {
- PlatformSetup platformSetup = PlatformSetupAccessor.getPlatformSetup();
+ PlatformSetup platformSetup = getPlatformSetup();
if (isDropOnStart()) {
platformSetup.destroy();
}
}
+ protected PlatformSetup getPlatformSetup() throws NamingException {
+ return PlatformSetupAccessor.getInstance().getPlatformSetup();
+ }
+
//-------------- engine life cycle methods
protected void prepareEnvironment() throws Exception {
diff --git a/bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListener.java b/bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListener.java
index 13742c3e61a..5d207c95d25 100644
--- a/bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListener.java
+++ b/bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListener.java
@@ -13,11 +13,13 @@
**/
package org.bonitasoft.engine.api.internal.servlet;
+import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.bonitasoft.engine.EngineInitializer;
import org.bonitasoft.engine.service.impl.ServiceAccessorFactory;
+import org.bonitasoft.platform.setup.PlatformSetup;
import org.bonitasoft.platform.setup.PlatformSetupAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,7 +58,7 @@ public void contextInitialized(final ServletContextEvent event) {
AnnotationConfigWebApplicationContext initializeWebApplicationContext(ServletContextEvent event,
EngineInitializer engineInitializer) throws Exception {
- PlatformSetupAccessor.getPlatformSetup().init(); // init tables and default configuration
+ getPlatformSetup().init(); // init tables and default configuration
engineInitializer.initializeEngine();
ApplicationContext engineContext = ServiceAccessorFactory.getInstance()
.createServiceAccessor()
@@ -67,6 +69,10 @@ AnnotationConfigWebApplicationContext initializeWebApplicationContext(ServletCon
return webApplicationContext;
}
+ protected PlatformSetup getPlatformSetup() throws NamingException {
+ return PlatformSetupAccessor.getInstance().getPlatformSetup();
+ }
+
void exit(int code) {
System.exit(code);
}
diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java
index 1c307fb7fc9..4dac4892cd5 100755
--- a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java
+++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java
@@ -100,7 +100,7 @@ default Application getApplication(final long applicationId) throws ApplicationN
} else {
throw new ApplicationNotFoundException(applicationId);
}
- };
+ }
/**
* Retrieves an {@link IApplication} from its identifier.
diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java
index fa3fa91ed58..982e11ce9d6 100644
--- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java
+++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java
@@ -16,8 +16,14 @@
import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;
import java.io.Serializable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.bonitasoft.engine.SArchivingException;
@@ -56,15 +62,38 @@
import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;
import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionException;
import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException;
-import org.bonitasoft.engine.core.process.definition.model.*;
+import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SContractDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SDocumentDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;
+import org.bonitasoft.engine.core.process.definition.model.SGatewayDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SGatewayType;
+import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;
+import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;
+import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;
import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;
import org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService;
import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;
import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;
-import org.bonitasoft.engine.core.process.instance.api.exceptions.*;
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException;
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException;
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException;
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException;
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;
import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState;
-import org.bonitasoft.engine.core.process.instance.model.*;
+import org.bonitasoft.engine.core.process.instance.model.SActivityInstance;
+import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;
+import org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType;
+import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;
+import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;
+import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
+import org.bonitasoft.engine.core.process.instance.model.SStateCategory;
import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;
import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;
@@ -78,7 +107,6 @@
import org.bonitasoft.engine.execution.event.EventsHandler;
import org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper;
import org.bonitasoft.engine.execution.handler.SProcessInstanceHandler;
-import org.bonitasoft.engine.execution.state.FlowNodeStateManager;
import org.bonitasoft.engine.execution.work.BPMWorkFactory;
import org.bonitasoft.engine.expression.Expression;
import org.bonitasoft.engine.expression.ExpressionService;
@@ -120,7 +148,7 @@ public class ProcessExecutorImpl implements ProcessExecutor {
private final ProcessDefinitionService processDefinitionService;
private final GatewayInstanceService gatewayInstanceService;
private final OperationService operationService;
- private ProcessResourcesService processResourcesService;
+ private final ProcessResourcesService processResourcesService;
private final ConnectorInstanceService connectorInstanceService;
private final TransitionEvaluator transitionEvaluator;
private final ContractDataService contractDataService;
@@ -129,6 +157,7 @@ public class ProcessExecutorImpl implements ProcessExecutor {
private final DocumentHelper documentHelper;
private final BPMWorkFactory workFactory;
private final BPMArchiverService bpmArchiverService;
+ private final ProcessStarterVerifier processStarterVerifier;
public ProcessExecutorImpl(final ActivityInstanceService activityInstanceService,
final ProcessInstanceService processInstanceService, final FlowNodeExecutor flowNodeExecutor,
@@ -142,11 +171,11 @@ public ProcessExecutorImpl(final ActivityInstanceService activityInstanceService
final EventService eventService,
final Map> handlers, final DocumentService documentService,
final ContainerRegistry containerRegistry, final BPMInstancesCreator bpmInstancesCreator,
- final EventsHandler eventsHandler, final FlowNodeStateManager flowNodeStateManager,
+ final EventsHandler eventsHandler,
final BusinessDataRepository businessDataRepository,
final RefBusinessDataService refBusinessDataService, final TransitionEvaluator transitionEvaluator,
final ContractDataService contractDataService, BPMWorkFactory workFactory,
- BPMArchiverService bpmArchiverService) {
+ BPMArchiverService bpmArchiverService, final ProcessStarterVerifier processStarterVerifier) {
super();
this.activityInstanceService = activityInstanceService;
this.processInstanceService = processInstanceService;
@@ -169,6 +198,7 @@ public ProcessExecutorImpl(final ActivityInstanceService activityInstanceService
this.contractDataService = contractDataService;
this.workFactory = workFactory;
this.bpmArchiverService = bpmArchiverService;
+ this.processStarterVerifier = processStarterVerifier;
documentHelper = new DocumentHelper(documentService, processDefinitionService, processInstanceService);
//FIXME There is responsibility issue the circular dependencies must be fixed next time.
eventsHandler.setProcessExecutor(this);
@@ -914,8 +944,7 @@ protected SProcessInstance start(final long starterId, final long starterSubstit
final List operations, final Map context,
final List connectors,
final long callerId, final FlowNodeSelector selector, final Map processInputs)
- throws SProcessInstanceCreationException,
- SContractViolationException {
+ throws SProcessInstanceCreationException, SContractViolationException {
final SProcessDefinition sProcessDefinition = selector.getProcessDefinition();
@@ -945,7 +974,9 @@ protected SProcessInstance start(final long starterId, final long starterSubstit
// we stop execution here
return sProcessInstance;
}
- return startElements(sProcessInstance, selector);
+ final SProcessInstance processInstance = startElements(sProcessInstance, selector);
+ processStarterVerifier.verify(processInstance);
+ return processInstance;
} catch (final SProcessInstanceCreationException e) {
throw e;
} catch (final SBonitaException e) {
diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifier.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifier.java
new file mode 100644
index 00000000000..814af4fa309
--- /dev/null
+++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifier.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2024 Bonitasoft S.A.
+ * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
+ * This library is free software; you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+ * Floor, Boston, MA 02110-1301, USA.
+ **/
+package org.bonitasoft.engine.execution;
+
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;
+import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
+
+/**
+ * Define rules to be executed before the start of a process, right after its creation.
+ */
+public interface ProcessStarterVerifier {
+
+ /**
+ * Verify that a process is ready to be started right after its creation.
+ *
+ * @param processInstance the process instance that is going to be started
+ * @throws SProcessInstanceCreationException if the process is not in a valid state to start
+ */
+ void verify(SProcessInstance processInstance) throws SProcessInstanceCreationException;
+}
diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifierImpl.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifierImpl.java
new file mode 100644
index 00000000000..6d56d989a51
--- /dev/null
+++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifierImpl.java
@@ -0,0 +1,179 @@
+/**
+ * Copyright (C) 2024 Bonitasoft S.A.
+ * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
+ * This library is free software; you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+ * Floor, Boston, MA 02110-1301, USA.
+ **/
+package org.bonitasoft.engine.execution;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;
+import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
+import org.bonitasoft.engine.platform.PlatformRetriever;
+import org.bonitasoft.engine.platform.exception.SPlatformNotFoundException;
+import org.bonitasoft.engine.platform.exception.SPlatformUpdateException;
+import org.bonitasoft.engine.service.platform.PlatformInformationService;
+import org.bonitasoft.engine.transaction.TransactionService;
+import org.bonitasoft.platform.setup.SimpleEncryptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConditionalOnSingleCandidate(ProcessStarterVerifier.class)
+@Slf4j
+public class ProcessStarterVerifierImpl implements ProcessStarterVerifier {
+
+ public static final int LIMIT = 150;
+ protected static final int PERIOD_IN_DAYS = 30;
+ protected static final long PERIOD_IN_MILLIS = PERIOD_IN_DAYS * 24L * 60L * 60L * 1000L;
+ protected static final List THRESHOLDS_IN_PERCENT = List.of(80, 90);
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ private final PlatformRetriever platformRetriever;
+ private final PlatformInformationService platformInformationService;
+
+ private final List counters = Collections.synchronizedList(new ArrayList<>());
+
+ ProcessStarterVerifierImpl(PlatformRetriever platformRetriever,
+ PlatformInformationService platformInformationService) {
+ this.platformRetriever = platformRetriever;
+ this.platformInformationService = platformInformationService;
+ }
+
+ @Autowired
+ ProcessStarterVerifierImpl(PlatformRetriever platformRetriever,
+ PlatformInformationService platformInformationService,
+ TransactionService transactionService) throws Exception {
+ this(platformRetriever, platformInformationService);
+ counters.addAll(transactionService.executeInTransaction(this::readCounters));
+ }
+
+ protected List getCounters() {
+ return Collections.unmodifiableList(counters);
+ }
+
+ protected void addCounter(long counter) {
+ synchronized (counters) {
+ counters.add(counter);
+ }
+ }
+
+ @Override
+ public void verify(SProcessInstance processInstance) throws SProcessInstanceCreationException {
+ log.debug("Verifying process instance {}", processInstance.getId());
+ cleanupOldValues();
+ log.debug("Found {} cases already started in the last {} days", counters.size(), PERIOD_IN_DAYS);
+ if (counters.size() >= LIMIT) {
+ final String nextValidTime = getStringRepresentation(getNextResetTimestamp(counters));
+ throw new SProcessInstanceCreationException(
+ format("Process start limit (%s cases during last %s days) reached. You are not allowed to start a new process until %s.",
+ LIMIT, PERIOD_IN_DAYS, nextValidTime));
+ }
+ try {
+ synchronized (counters) {
+ counters.add(System.currentTimeMillis());
+ }
+ final String information = encryptDataBeforeSendingToDatabase(counters);
+ // store in database:
+ storeNewValueInDatabase(information);
+ logCaseLimitProgressIfThresholdReached();
+ } catch (IOException | SPlatformNotFoundException | SPlatformUpdateException e) {
+ log.trace(e.getMessage(), e);
+ throw new SProcessInstanceCreationException(
+ format("Unable to start the process instance %s", processInstance.getId()));
+ }
+ }
+
+ void cleanupOldValues() {
+ log.trace("Cleaning up old values for the last {} days", PERIOD_IN_DAYS);
+ final long oldestValidTimestamp = System.currentTimeMillis() - PERIOD_IN_MILLIS;
+ synchronized (counters) {
+ counters.removeIf(timestamp -> timestamp < oldestValidTimestamp);
+ }
+ }
+
+ void storeNewValueInDatabase(String information) throws SPlatformUpdateException, SPlatformNotFoundException {
+ platformInformationService.updatePlatformInfo(platformRetriever.getPlatform(), information);
+ }
+
+ List readCounters() {
+ try {
+ String information = platformRetriever.getPlatform().getInformation();
+ if (information == null || information.isBlank()) {
+ throw new IllegalStateException("Invalid database. Please reset it and restart.");
+ }
+ return decryptDataFromDatabase(information);
+ } catch (SPlatformNotFoundException | IOException e) {
+ throw new IllegalStateException("Cannot read from database table 'platform'", e);
+ }
+ }
+
+ String encryptDataBeforeSendingToDatabase(List counters) throws IOException {
+ return encrypt(OBJECT_MAPPER.writeValueAsBytes(counters));
+ }
+
+ List decryptDataFromDatabase(String information) throws IOException {
+ return OBJECT_MAPPER.readValue(decrypt(information), new TypeReference<>() {
+ });
+ }
+
+ private static String encrypt(byte[] data) {
+ try {
+ return SimpleEncryptor.encrypt(data);
+ } catch (GeneralSecurityException e) {
+ throw new IllegalStateException("Cannot cipher information", e);
+ }
+ }
+
+ private static byte[] decrypt(String information) {
+ try {
+ return SimpleEncryptor.decrypt(information);
+ } catch (GeneralSecurityException e) {
+ throw new IllegalStateException("Cannot decipher information", e);
+ }
+ }
+
+ void logCaseLimitProgressIfThresholdReached() {
+ var percentBeforeThisNewCase = (float) ((getCounters().size() - 1) * 100) / LIMIT;
+ var percentWithThisNewCase = (float) ((getCounters().size()) * 100) / LIMIT;
+ for (Integer threshold : THRESHOLDS_IN_PERCENT) {
+ if (percentBeforeThisNewCase < threshold && percentWithThisNewCase >= threshold) {
+ log.warn("You have started {}% of your allowed cases."
+ + "If you need more volume, please consider subscribing to an Enterprise edition.",
+ threshold);
+ }
+ }
+ }
+
+ /**
+ * Returns a timestamp to a human-readable format
+ */
+ private String getStringRepresentation(long timestamp) {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timestamp));
+ }
+
+ private long getNextResetTimestamp(List timestamps) {
+ return Collections.min(timestamps) + PERIOD_IN_MILLIS;
+ }
+
+}
diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationService.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationService.java
new file mode 100644
index 00000000000..5b0113c0ac8
--- /dev/null
+++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationService.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2019 Bonitasoft S.A.
+ * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
+ * This library is free software; you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+ * Floor, Boston, MA 02110-1301, USA.
+ **/
+package org.bonitasoft.engine.service.platform;
+
+import org.bonitasoft.engine.platform.exception.SPlatformUpdateException;
+import org.bonitasoft.engine.platform.model.SPlatform;
+
+/**
+ * @author Elias Ricken de Medeiros
+ */
+public interface PlatformInformationService {
+
+ /**
+ * Updates the platform information
+ *
+ * @param platform the platform to be updated
+ * @param platformInfo the new platform information
+ * @throws SPlatformUpdateException
+ * @since 7.1.0
+ */
+ void updatePlatformInfo(SPlatform platform, String platformInfo) throws SPlatformUpdateException;
+
+}
diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImpl.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImpl.java
new file mode 100644
index 00000000000..10fcc891cf1
--- /dev/null
+++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImpl.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2019 Bonitasoft S.A.
+ * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
+ * This library is free software; you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+ * Floor, Boston, MA 02110-1301, USA.
+ **/
+package org.bonitasoft.engine.service.platform;
+
+import org.bonitasoft.engine.platform.exception.SPlatformUpdateException;
+import org.bonitasoft.engine.platform.model.SPlatform;
+import org.bonitasoft.engine.services.PersistenceService;
+import org.bonitasoft.engine.services.SPersistenceException;
+import org.bonitasoft.engine.services.UpdateDescriptor;
+import org.springframework.stereotype.Service;
+
+/**
+ * Updates the platform information using directly the {@link PersistenceService}
+ *
+ * @author Elias Ricken de Medeiros
+ */
+@Service("platformInformationService")
+public class PlatformInformationServiceImpl implements PlatformInformationService {
+
+ private final PersistenceService persistenceService;
+
+ public PlatformInformationServiceImpl(final PersistenceService persistenceService) {
+ this.persistenceService = persistenceService;
+ }
+
+ @Override
+ public void updatePlatformInfo(final SPlatform platform, final String platformInfo)
+ throws SPlatformUpdateException {
+
+ UpdateDescriptor desc = new UpdateDescriptor(platform);
+ desc.addField(SPlatform.INFORMATION, platformInfo);
+
+ try {
+ persistenceService.update(desc);
+ } catch (SPersistenceException e) {
+ throw new SPlatformUpdateException(e);
+ }
+ }
+
+}
diff --git a/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml b/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml
index 1517e211ddf..2bd72f8ffef 100644
--- a/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml
+++ b/bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml
@@ -1226,12 +1226,12 @@
-
+
processInputs = new HashMap<>(0);
@@ -334,8 +338,8 @@ public void startProcessWithOperationsAndContextAndExpressionContextAndConnector
doReturn(sProcessDefinitionDeployInfo).when(processDefinitionService).getProcessDeploymentInfo(anyLong());
when(mockedProcessExecutorImpl.startElements(eq(sProcessInstance), eq(selector))).thenReturn(sProcessInstance);
when(mockedProcessExecutorImpl.createProcessInstance(sProcessDefinition, starterId, starterSubstituteId, 1L))
- .thenReturn(
- sProcessInstance);
+ .thenReturn(sProcessInstance);
+ doNothing().when(processStarterVerifier).verify(sProcessInstance);
final Map processInputs = new HashMap<>(0);
doNothing().when(mockedProcessExecutorImpl).validateContractInputs(processInputs, sProcessDefinition);
diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/ProcessStarterVerifierTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/ProcessStarterVerifierTest.java
new file mode 100644
index 00000000000..27f95430ffa
--- /dev/null
+++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/execution/ProcessStarterVerifierTest.java
@@ -0,0 +1,199 @@
+/**
+ * Copyright (C) 2024 Bonitasoft S.A.
+ * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
+ * This library is free software; you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+ * Floor, Boston, MA 02110-1301, USA.
+ **/
+package org.bonitasoft.engine.execution;
+
+import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.bonitasoft.engine.execution.ProcessStarterVerifierImpl.LIMIT;
+import static org.bonitasoft.engine.execution.ProcessStarterVerifierImpl.PERIOD_IN_MILLIS;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException;
+import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
+import org.bonitasoft.engine.platform.PlatformRetriever;
+import org.bonitasoft.engine.platform.model.SPlatform;
+import org.bonitasoft.engine.service.platform.PlatformInformationService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ProcessStarterVerifierTest {
+
+ @Mock
+ private PlatformRetriever platformRetriever;
+
+ @Mock
+ private PlatformInformationService platformInformationService;
+
+ ProcessStarterVerifierImpl processStarterVerifier;
+
+ @BeforeEach
+ void setUp() {
+ processStarterVerifier = Mockito
+ .spy(new ProcessStarterVerifierImpl(platformRetriever, platformInformationService));
+ }
+
+ @Test
+ void should_be_able_to_decrypt_encrypted_value() throws Exception {
+ //given
+ final long currentTime = System.currentTimeMillis();
+ final var originalValue = List.of(currentTime, currentTime + 1000L, currentTime + 2000L);
+
+ //when
+ final List computedValue = processStarterVerifier
+ .decryptDataFromDatabase(processStarterVerifier.encryptDataBeforeSendingToDatabase(originalValue));
+
+ //then
+ assertThat(computedValue).isEqualTo(originalValue);
+ }
+
+ @Test
+ void verify_should_not_remove_still_valid_values_from_counters() throws Exception {
+ //given
+ final long validTimestamp = System.currentTimeMillis() - PERIOD_IN_MILLIS + 86400000L; // plus 1 day
+ processStarterVerifier.addCounter(validTimestamp);
+ doNothing().when(processStarterVerifier).storeNewValueInDatabase(anyString());
+
+ //when
+ processStarterVerifier.verify(new SProcessInstance());
+
+ //then
+ assertThat(processStarterVerifier.getCounters()).size().isEqualTo(2);
+ assertThat(processStarterVerifier.getCounters()).contains(validTimestamp);
+ }
+
+ @Test
+ void verify_should_remove_old_values_from_counters() throws Exception {
+ //given
+ final long obsoleteValue = System.currentTimeMillis() - PERIOD_IN_MILLIS - 86400000L; // minus 1 day
+ processStarterVerifier.addCounter(obsoleteValue);
+ doNothing().when(processStarterVerifier).storeNewValueInDatabase(anyString());
+
+ //when
+ processStarterVerifier.verify(new SProcessInstance());
+
+ //then
+ assertThat(processStarterVerifier.getCounters()).size().isEqualTo(1);
+ assertThat(processStarterVerifier.getCounters()).doesNotContain(obsoleteValue);
+ }
+
+ @Test
+ void verify_should_throw_exception_if_limit_is_reached() throws Exception {
+ //given
+ for (int i = 0; i < LIMIT; i++) {
+ processStarterVerifier.addCounter(System.currentTimeMillis());
+ }
+
+ //when - then
+ assertThatExceptionOfType(SProcessInstanceCreationException.class)
+ .isThrownBy(() -> processStarterVerifier.verify(new SProcessInstance()))
+ .withMessageContaining("Process start limit");
+ assertThat(processStarterVerifier.getCounters()).size().isEqualTo(LIMIT);
+ verify(processStarterVerifier, never()).storeNewValueInDatabase(anyString());
+ }
+
+ @Test
+ void should_log_when_80_percent_is_reached() throws Exception {
+ var counters = mock(List.class);
+ doReturn(LIMIT * 80 / 100).when(counters).size();
+ doReturn(counters).when(processStarterVerifier).getCounters();
+
+ // when
+ final String log = tapSystemOut(() -> processStarterVerifier.logCaseLimitProgressIfThresholdReached());
+
+ // then
+ assertThat(log).containsPattern("WARN.*80%");
+ }
+
+ @Test
+ void should_log_when_90_percent_is_reached() throws Exception {
+ var counters = mock(List.class);
+ doReturn(LIMIT * 90 / 100).when(counters).size();
+ doReturn(counters).when(processStarterVerifier).getCounters();
+
+ // when
+ final String log = tapSystemOut(() -> processStarterVerifier.logCaseLimitProgressIfThresholdReached());
+
+ // then
+ assertThat(log)
+ .containsPattern("WARN.*90%")
+ .doesNotContain("80%");
+ }
+
+ @Test
+ void should_not_log_when_80_percent_has_already_been_reached() throws Exception {
+ var counters = mock(List.class);
+ doReturn(121).when(counters).size();
+ doReturn(counters).when(processStarterVerifier).getCounters();
+
+ // when
+ final String log = tapSystemOut(() -> processStarterVerifier.logCaseLimitProgressIfThresholdReached());
+
+ // then
+ assertThat(log).isBlank();
+ }
+
+ @Test
+ void readCounters_should_fail_if_counters_are_not_set() throws Exception {
+ // given
+ final SPlatform platform = new SPlatform();
+ platform.setInformation("");
+ doReturn(platform).when(platformRetriever).getPlatform();
+
+ // when - then
+ assertThatExceptionOfType(IllegalStateException.class)
+ .isThrownBy(() -> processStarterVerifier.readCounters());
+ }
+
+ @Test
+ void readCounters_should_fail_if_counters_cannot_be_decrypted_from_database() throws Exception {
+ // given
+ final SPlatform platform = new SPlatform();
+ final String encryptedValue = "encrypted value";
+ platform.setInformation(encryptedValue);
+ doReturn(platform).when(platformRetriever).getPlatform();
+ doThrow(new IOException("Cannot decipher information")).when(processStarterVerifier)
+ .decryptDataFromDatabase(encryptedValue);
+
+ // when - then
+ assertThatExceptionOfType(IllegalStateException.class)
+ .isThrownBy(() -> processStarterVerifier.readCounters());
+ }
+
+ @Test
+ void readCounters_should_succeed_if_counters_can_be_decrypted_from_database() throws Exception {
+ // given
+ final SPlatform platform = new SPlatform();
+ final String encryptedValue = "encrypted value";
+ platform.setInformation(encryptedValue);
+ doReturn(platform).when(platformRetriever).getPlatform();
+ final List countersFromDatabase = List.of(System.currentTimeMillis());
+ doReturn(countersFromDatabase).when(processStarterVerifier).decryptDataFromDatabase(encryptedValue);
+
+ // when
+ final List counters = processStarterVerifier.readCounters();
+
+ // then
+ assertThat(counters).isEqualTo(countersFromDatabase);
+ }
+}
diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImplTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImplTest.java
new file mode 100644
index 00000000000..e54a68d7618
--- /dev/null
+++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImplTest.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2019 Bonitasoft S.A.
+ * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
+ * This library is free software; you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+ * Floor, Boston, MA 02110-1301, USA.
+ **/
+package org.bonitasoft.engine.service.platform;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+import org.bonitasoft.engine.platform.exception.SPlatformUpdateException;
+import org.bonitasoft.engine.platform.model.SPlatform;
+import org.bonitasoft.engine.services.PersistenceService;
+import org.bonitasoft.engine.services.SPersistenceException;
+import org.bonitasoft.engine.services.UpdateDescriptor;
+import org.hamcrest.core.IsEqual;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PlatformInformationServiceImplTest {
+
+ @Mock
+ private PersistenceService persistenceService;
+
+ @InjectMocks
+ private PlatformInformationServiceImpl platformInformationService;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void updatePlatformInfo() throws Exception {
+ //given
+ SPlatform platform = mock(SPlatform.class);
+
+ //when
+ platformInformationService.updatePlatformInfo(platform, "info");
+
+ //then
+ ArgumentCaptor upDescCaptor = ArgumentCaptor.forClass(UpdateDescriptor.class);
+ verify(persistenceService).update(upDescCaptor.capture());
+ UpdateDescriptor descriptor = upDescCaptor.getValue();
+ assertThat(descriptor.getFields()).containsEntry(SPlatform.INFORMATION, "info");
+ assertThat(descriptor.getEntity()).isEqualTo(platform);
+ }
+
+ @Test
+ public void updatePlatformInfo_should_throw_exception_when_persistence_service_throws_exception() throws Exception {
+ //given
+ SPlatform platform = mock(SPlatform.class);
+ SPersistenceException toBeThrown = new SPersistenceException("exception");
+ doThrow(toBeThrown).when(persistenceService).update(any(UpdateDescriptor.class));
+
+ //then
+ expectedException.expect(SPlatformUpdateException.class);
+ expectedException.expectCause(new IsEqual(toBeThrown));
+
+ //when
+ platformInformationService.updatePlatformInfo(platform, "info");
+ }
+}
diff --git a/bpm/bonita-synchro-repository/bonita-synchro-register/build.gradle b/bpm/bonita-synchro-repository/bonita-synchro-register/build.gradle
index 9807d2e4ad4..8a990f631b4 100644
--- a/bpm/bonita-synchro-repository/bonita-synchro-register/build.gradle
+++ b/bpm/bonita-synchro-repository/bonita-synchro-register/build.gradle
@@ -4,6 +4,8 @@ dependencies {
api project(':bpm:bonita-core:bonita-process-engine')
api project(':bpm:bonita-synchro-repository:bonita-synchro-service-impl')
api project(':services:bonita-commons')
+ annotationProcessor(libs.lombok)
+ compileOnly(libs.lombok)
compileOnly 'jakarta.jms:jakarta.jms-api:3.0.0'
compileOnly 'org.apache.activemq:activemq-client:5.16.6'
}
diff --git a/bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/AbstractSynchroService.java b/bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/AbstractSynchroService.java
index 1ce41ef9dfd..467b2f8d478 100644
--- a/bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/AbstractSynchroService.java
+++ b/bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/AbstractSynchroService.java
@@ -111,8 +111,6 @@ protected Serializable getFiredAndRemoveIt(final Map expec
List> firedEvents = cacheService.getKeys(SYNCHRO_SERVICE_CACHE);
for (Map firedEvent : (List